Skip to content Skip to sidebar Skip to footer

With-statement And Threading :making Function Execute Before Run

This question is a follow up from following question:With statement and python threading I have been experimenting with python threading api. I have this code which works for what

Solution 1:

The problem here is that you want the run function to wait until the execute function is called.

Of course the obvious solution is to call execute before you call start:

t1.execute(print_time,5,1)
t2.execute(print_time,5,2)
with contextlib.nested(t1, t2):
    pass

… or just make execute call start, or pass the function in to the constructor or the start call, or…

Also, your intended design is a bit weird. The thread function is designed to handle the case where _f hasn't been set… but you want it to wait until _f has been set?


But it's conceivable that this kind of problem could come up in a more realistic design, so, let's look at how to solve it.

First, adding sleep to solve a threading problem is almost always a sign that you're doing something very wrong. It's also a great way to cause either horrible performance problems (as in: by the time you add enough sleeps in enough places to make everything mostly work, it takes 30 seconds for your app to start instead of 30 milliseconds)—and, worse, race-condition bugs (surely 1 second is always enough time, right? unless the computer is thrashing swap, or waking up from hibernate, or busy with other programs using all the CPU, or…).

If you're trying to synchronize actions across threads, you need to use a synchronization object. The trick is knowing the right one. Read the docs for Lock through Event (and 3.x adds Barrier), and find a tutorial on threading in general to get a broader idea of what all of these things are for.*

In this case, you've got code that's waiting for some change to saved state, and other code that's making the change, which is the prototypical use case for a 'Condition'. So:

classMythread(threading.Thread):def__init__(self, threadID, name, condition):
        self.condition = condition
        # ... same as beforedefrun(self):
        # ... setup before checking for _f

        with self.condition:whilenotself._f:self.condition.wait()
        self._f()

        # ... anything else you want

Now, you need to create the Condition, pass it to the threads, and notify it.

You could use a single Condition:

condition= threading.Condition()
t1 = Mythread(1, "Thread1", condition)
t2 = Mythread(2, "Thread2", condition)
with contextlib.nested(t1,t2):
    withcondition:
        t1.execute(print_time, 5, 1)
        t2.execute(print_time, 5, 2)
        condition.notify_all()

Alternatively, you can give each thread its own Condition:

class Mythread(threading.Thread):
    def __init__(self, threadID, name):
        self.condition = Condition()
        # ... same as before

# ...

t1 = Mythread(1, "Thread1")
t2 = Mythread(2, "Thread2")
with contextlib.nested(t1,t2):
    with t1.condition:
        t1.execute(print_time, 5, 1)
        t1.condition.notify()
    with t2.condition:
        t2.execute(print_time, 5, 1)
        t2.condition.notify()

Note that this doesn't allow you to explicit "not set" _f, but it's pretty easy to do that. For example, you can add an _f_set attribute, and check that instead of _f, so someone can call execute(None) (and then notify) to wake you up and get you to the "no _f" case.


* Warning: Some of the naming is inconsistent. There's a different thing also called "barrier", and a different different thing also called "fence", and there are many variants of "event" that are pretty different from Pythons (some of which are more like a condition, but aren't actually usable as such), and sometimes a "condition variable" is the actual shared state protected by the sync object rather than the sync object, and so on…

Post a Comment for "With-statement And Threading :making Function Execute Before Run"