3

I'm new to Python so please bear with my question.

Let's say my application has a module named message_printer which simply defines a print_message function to print the message. Now in my main file, I create two threads which calls print function in message_printer.

My question is: How can I set a different message per thread and access it in message_printer?

message_printer:

import threading

threadLocal = threading.local()

def print_message():
   name = getattr(threadLocal, 'name', None);
   print name
   return

main:

import threading
import message_printer

threadLocal = threading.local()

class Executor (threading.Thread):
    def __init__(self, name):
        threading.Thread.__init__(self)
        threadLocal.name = name

    def run(self):
        message_printer.print_message();

A = Executor("A");
A.start();
B = Executor("B");
B.start();

This just outputs None and None while I expect A and B. I also tried accessing threadLocal object inside the print_message function directly but doesn't work.

Note that this is just an example. In my application, the exact use case is for logging. Main launches a bunch of thread which call other modules. I want to have a different logger per thread (each thread should log to its own file) and each logger needs to be configured in Main. So I'm trying to instantiate logger per thread and set in thread local storage which can then be accessed in other modules.

What am I doing wrong? I'm following this question as an example Thread local storage in Python

RandomQuestion
  • 6,778
  • 17
  • 61
  • 97

2 Answers2

3

The problem with your code, is that you are not assigning your name to the correct local() context. Your __init__() method is run in the main thread, before you start your A and B threads by calling .start().

Your first thread creation A = Executor("A"); will create a new thread A but update the local context of the main thread. Then, when you start A by calling A.start(); you will enter A:s context, with a separate local context. Here name is not defined and you end up with None as output. The same then happens for B.

In other words, to access the thread local variables you should be running the current thread, which you are when running .start() (which will call your .run() method), but not when creating the objects (running __init__()).

To get your current code working, you could store the data in each object (using self references) and then, when each thread is running, copy the content to the thread local context:

import threading

threadLocal = threading.local()

def print_message():
   name = getattr(threadLocal, 'name', None);
   print name
   return

class Executor (threading.Thread):
    def __init__(self, name):
        threading.Thread.__init__(self)
        # Store name in object using self reference
        self.name = name

    def run(self):
        # Here we copy from object to local context,
        # since the thread is running
        threadLocal.name = self.name
        print_message();

A = Executor("A")
A.start()
B = Executor("B")
B.start()

Note, though, in this situation, it is somewhat of an overkill to use the thread local context, since we already store the separate data values in the different objects. To use it directly from the objects, would require a small rewrite of print_message() though.

JohanL
  • 6,671
  • 1
  • 12
  • 26
  • Awesome! In my case, I need to access the object stored in thread local in a different module at multiple places. I wanted to avoid passing it around all the time as a parameter so seems thread local should fit my scenario. – RandomQuestion Aug 10 '17 at 07:58
1

I think this may be helpful for your use case. Another way on how thread storage can been done across files/modules.

Shivansh Jagga
  • 1,541
  • 1
  • 15
  • 24