I don't understand what is happening in this threading code

M

Matthew Wilson

In this code, I tried to kill my thread object by setting a variable on it
to False.

Inside the run method of my thread object, it checks a different
variable.

I've already rewritten this code to use semaphores, but I'm just curious
what is going on.

Here's the code:

import logging, threading, time
logging.basicConfig(level=logging.DEBUG,
format="%(threadName)s: %(message)s")

class Waiter(threading.Thread):
def __init__(self, hot_food):
super(Waiter, self).__init__()
self.should_keep_running = True
self.hot_food = hot_food

def run(self):
while self.should_keep_running:
logging.debug("Inside run, the id of should_keep_running is %s."
% id(self.should_keep_running))
self.hot_food.acquire()

def cook_food(hot_food):
i = 5
while i >= 0:
logging.debug("I am cooking food...")
time.sleep(1)
hot_food.release()
logging.debug("Andiamo!")
i -= 1

def main():

hot_food = threading.Semaphore(value=0)

chef = threading.Thread(name="chef", target=cook_food, args=(hot_food, ))
chef.start()

w = Waiter(hot_food)
logging.debug("Initially, the id of w.should_keep_running is %s."
% id(w.should_keep_running))
w.start()
logging.debug("After start, the id of w.should_keep_running is %s."
% id(w.should_keep_running))

# Wait for the chef to finish work.
chef.join()

# Now try to kill off the waiter by setting a variable inside the waiter.
w.should_keep_running = False
logging.debug("Now, the id of w.should_keep_running is %s."
% id(w.should_keep_running))

if __name__ == "__main__":
main()

And here's what I get when I execute it. I have to suspend the process
with CTRL=Z and then kill -9 it.

$ python foo.py
MainThread: Initially, the id of w.should_keep_running is 135527852.
MainThread: After start, the id of w.should_keep_running is 135527852.
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
Thread-1: Inside run, the id of should_keep_running is 135527852.
MainThread: Now, the id of w.should_keep_running is 135527840.

[1]+ Stopped python foo.py

$ kill -9 %1

[1]+ Stopped python foo.py

The memory address of should_keep_running seems to change when I set it
from True to False, and inside the run method, I keep checking the old
location.

I am totally baffled what this means.

Like I said earlier, I already rewrote this code to use semaphores, but
I just want to know what is going on here.

Any explanation is welcome.

TIA

Matt
 
C

Carl Banks

In this code, I tried to kill my thread object by setting a variable on it
to False.

Inside the run method of my thread object, it checks a different
variable.

I've already rewritten this code to use semaphores, but I'm just curious
what is going on.

Here's the code:

import logging, threading, time
logging.basicConfig(level=logging.DEBUG,
format="%(threadName)s: %(message)s")

class Waiter(threading.Thread):
def __init__(self, hot_food):
super(Waiter, self).__init__()
self.should_keep_running = True
self.hot_food = hot_food

def run(self):
while self.should_keep_running:
logging.debug("Inside run, the id of should_keep_running is %s."
% id(self.should_keep_running))
self.hot_food.acquire()

def cook_food(hot_food):
i = 5
while i >= 0:
logging.debug("I am cooking food...")
time.sleep(1)
hot_food.release()
logging.debug("Andiamo!")
i -= 1

def main():

hot_food = threading.Semaphore(value=0)

chef = threading.Thread(name="chef", target=cook_food, args=(hot_food, ))
chef.start()

w = Waiter(hot_food)
logging.debug("Initially, the id of w.should_keep_running is %s."
% id(w.should_keep_running))
w.start()
logging.debug("After start, the id of w.should_keep_running is %s."
% id(w.should_keep_running))

# Wait for the chef to finish work.
chef.join()

# Now try to kill off the waiter by setting a variable inside the waiter.
w.should_keep_running = False
logging.debug("Now, the id of w.should_keep_running is %s."
% id(w.should_keep_running))

if __name__ == "__main__":
main()


It looks like your program's hanging while the waiter is waits to
acquire another plate of hot food.

Quick and dirty solution is to have waiter timeout at intervals while
waiting for the chef and check the clock to see if his shift has
ended. Better solution is to rewrite the logic, which you did.


Carl Banks
 
S

Sergio Correia

This is what's happening:

1) The chef thread releases the sema
2) While the chef thread is saying "Andiamo", decreasing "i", and
ending the while loop, the waiter thread SERVES the dish and RUNS to
reacquire the lock
3) Back in the main loop, chef.join() is run, and then the waiter's
variable is changed.

But it's already too late. The waiter is already locked. He'll wait
and wait forever for another dish, which will never come. So you will
have to kill him. All for a race condition.

---
I've just read Carl's post (just b4 hitting submit).

I agree with him, rewrite the logic. IMO, the objects should be the
key, THEY are the ones that should be joined. You should run the chef
and waiter as daemons, and just do something like

orders.join()
dishes.join()

And when the chefs finishes with the orders and the waiter finishes
serving them, the program ends.

HTH,
Sergio


In this code, I tried to kill my thread object by setting a variable on it
to False.

Inside the run method of my thread object, it checks a different
variable.

I've already rewritten this code to use semaphores, but I'm just curious
what is going on.

Here's the code:

import logging, threading, time
logging.basicConfig(level=logging.DEBUG,
format="%(threadName)s: %(message)s")

class Waiter(threading.Thread):
def __init__(self, hot_food):
super(Waiter, self).__init__()
self.should_keep_running = True
self.hot_food = hot_food

def run(self):
while self.should_keep_running:
logging.debug("Inside run, the id of should_keep_running is %s."
% id(self.should_keep_running))
self.hot_food.acquire()

def cook_food(hot_food):
i = 5
while i >= 0:
logging.debug("I am cooking food...")
time.sleep(1)
hot_food.release()
logging.debug("Andiamo!")
i -= 1

def main():

hot_food = threading.Semaphore(value=0)

chef = threading.Thread(name="chef", target=cook_food, args=(hot_food, ))
chef.start()

w = Waiter(hot_food)
logging.debug("Initially, the id of w.should_keep_running is %s."
% id(w.should_keep_running))
w.start()
logging.debug("After start, the id of w.should_keep_running is %s."
% id(w.should_keep_running))

# Wait for the chef to finish work.
chef.join()

# Now try to kill off the waiter by setting a variable inside the waiter.
w.should_keep_running = False
logging.debug("Now, the id of w.should_keep_running is %s."
% id(w.should_keep_running))

if __name__ == "__main__":
main()

And here's what I get when I execute it. I have to suspend the process
with CTRL=Z and then kill -9 it.

$ python foo.py
MainThread: Initially, the id of w.should_keep_running is 135527852.
MainThread: After start, the id of w.should_keep_running is 135527852.
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
Thread-1: Inside run, the id of should_keep_running is 135527852.
MainThread: Now, the id of w.should_keep_running is 135527840.

[1]+ Stopped python foo.py

$ kill -9 %1

[1]+ Stopped python foo.py

The memory address of should_keep_running seems to change when I set it
from True to False, and inside the run method, I keep checking the old
location.

I am totally baffled what this means.

Like I said earlier, I already rewrote this code to use semaphores, but
I just want to know what is going on here.

Any explanation is welcome.

TIA

Matt
 
N

Nir

In this code, I tried to kill my thread object by setting a variable on it
to False.

Inside the run method of my thread object, it checks a different
variable.

I've already rewritten this code to use semaphores, but I'm just curious
what is going on.

Here's the code:

import logging, threading, time
logging.basicConfig(level=logging.DEBUG,
format="%(threadName)s: %(message)s")

class Waiter(threading.Thread):
def __init__(self, hot_food):
super(Waiter, self).__init__()
self.should_keep_running = True
self.hot_food = hot_food

def run(self):
while self.should_keep_running:
logging.debug("Inside run, the id of should_keep_running is %s."
% id(self.should_keep_running))
self.hot_food.acquire()

def cook_food(hot_food):
i = 5
while i >= 0:
logging.debug("I am cooking food...")
time.sleep(1)
hot_food.release()
logging.debug("Andiamo!")
i -= 1

def main():

hot_food = threading.Semaphore(value=0)

chef = threading.Thread(name="chef", target=cook_food, args=(hot_food, ))
chef.start()

w = Waiter(hot_food)
logging.debug("Initially, the id of w.should_keep_running is %s."
% id(w.should_keep_running))
w.start()
logging.debug("After start, the id of w.should_keep_running is %s."
% id(w.should_keep_running))

# Wait for the chef to finish work.
chef.join()

# Now try to kill off the waiter by setting a variable inside the waiter.
w.should_keep_running = False
logging.debug("Now, the id of w.should_keep_running is %s."
% id(w.should_keep_running))

if __name__ == "__main__":
main()

And here's what I get when I execute it. I have to suspend the process
with CTRL=Z and then kill -9 it.

$ python foo.py
MainThread: Initially, the id of w.should_keep_running is 135527852.
MainThread: After start, the id of w.should_keep_running is 135527852.
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
chef: I am cooking food...
Thread-1: Inside run, the id of should_keep_running is 135527852.
chef: Andiamo!
Thread-1: Inside run, the id of should_keep_running is 135527852.
MainThread: Now, the id of w.should_keep_running is 135527840.

[1]+ Stopped python foo.py

$ kill -9 %1

[1]+ Stopped python foo.py

The memory address of should_keep_running seems to change when I set it
from True to False, and inside the run method, I keep checking the old
location.

I am totally baffled what this means.

Like I said earlier, I already rewrote this code to use semaphores, but
I just want to know what is going on here.

Any explanation is welcome.

TIA

Matt

It does not check a different variable.
You can open a Python session and try the following:

Python 2.5.1 (r251:54863, Oct 5 2007, 13:36:32)
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.135527840

The problem is that the waiter is locked waiting for new food and
never looks at the variable.
You should add hot_food.release() after you set the keep running flag
to False.

Nir
 
T

Tim Roberts

Matthew Wilson said:
In this code, I tried to kill my thread object by setting a variable on it
to False.

Inside the run method of my thread object, it checks a different
variable.

I've already rewritten this code to use semaphores, but I'm just curious
what is going on.
...
The memory address of should_keep_running seems to change when I set it
from True to False, and inside the run method, I keep checking the old
location.

You misunderstand what "id(a)" does. "id" doesn't tell you anything about
the name "a" or the address of "a". What it tells you is the unique
identifier of the object that "a" is bound to. Any time you change "a",
"id(a)" will also change.

Note, for example:

C:\tmp>python
Python 2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
504958236

504958236 is the id of "True", not of "a".
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top