OK the code is pretty basic. Since I'm using multiple threads, and I want shared variables between them, I'm using a global.
Why does the code in ThreadClass sometimes not execute when I hit "C"? I know it's a concurrency problem, but I'm not sure how to fix it. I've reading up on semaphores and locking lately, but I'm not exactly sure how to implement it at the moment. Any suggestions are welcome.
import threading
buff_list = []
class ThreadClass(threading.Thread):
global buff_list
def run(self):
while (True):
if ("C" == raw_input()):
buff_list.append("C")
print buff_list
class ThreadClass2(threading.Thread):
global buff_list
def run(self):
while(True):
if ("B" == raw_input() and len(buff_list) > 0):
buff_list.pop()
print buff_list
a = ThreadClass()
b = ThreadClass2()
a.start()
b.start()
You have two synchronization problems here.
Let's deal with the easier one first, the fact that you're sharing a global buff_list
that the two threads fight over. There's nothing stopping one thread from trying to append
at the same time the other thread pop
s, which is illegal. And, even if you get lucky and that doesn't happen, the pop
could come before the append
.
The simplest way to solve this is to use a Queue
, which is automatically-synchronizing:
buff_list = Queue.Queue()
Then just use put
instead of append
, and get
instead of pop
.
However, if you want to learn how to this stuff yourself, there are two possible ways to go.
First, you can use a Lock
. (You can also use an RLock
, but let's forget that for now.) This makes sure that only one thread is accessing buff_list
at a time.
buff_lock = threading.Lock()
buff_list = []
Now, whenever you append or pop, just grab the lock:
with buff_lock:
buff_list.append("C")
with buff_lock:
val = buff_list.pop()
But this won't make sure the popping code waits until there's something to pop. If you want to do that, use a Condition
:
buff_cond = threading.Condition()
Now:
with buff_cond:
buff_list.append("C")
buff_cond.notify()
with buff_cond:
while not buff_list:
buff_cond.wait()
value = buff_list.pop()
The second problem is that you're implicitly sharing sys.stdin
, because both threads are calling raw_input
. Unless you have some way to synchronize things so that each thread knows when it's supposed to get the next input (and that may be hard to even describe, and you can't turn it into code if you can't describe it), that can't possibly work—every time you type C
there's a 50/50 chance that the wrong thread will get it.
So, as kirelagin suggests, you need to make exactly one thread responsible for the I/O. The easiest way to do this is again to use a Queue
, and have one thread put
any inputs it doesn't use, and the other thread can get
from the queue.
Well you never know instance of which class got your input. If you hit “C” and that was ThreadClass2
reading your input, it will just do nothing since "B" == raw_input()
will be False
.
Exactly one thread should be responsible for I/O.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With