2

Does the following code thread-safe?
Will only one/first thread set the variable, set_this_var_only_once?

set_this_var_only_once = None

def worker():
    global set_this_var_only_once
    if set_this_var_only_once is None:
        set_this_var_only_once = "not None"

for i in range(10):
    t = threading.Thread( target=worker )
    t.daemon=True
    t.start()
2
  • 2
    What if two or more workers check the condition before either of them sets the variable? Commented Oct 18, 2016 at 5:32
  • @machineyearning that is my worry Commented Oct 18, 2016 at 5:34

3 Answers 3

4

Absolutely not.

It is quite possible that two threads execute this line before executing the next one:

    if set_this_var_only_once is None:

After that, both threads will execute the next line:

        set_this_var_only_once = "not None"

You can use locks to prevent that:

lock = threading.Lock()

def worker():
    lock.acquire()
    if set_this_var_only_once is None:
        set_this_var_only_once = "not None"
    lock.release()

Only one thread will be able to acquire the lock. If another thread tries to acquire it while it is locked, the call to lock.acquire() will block and wait until the lock is released by the first thread. Then the lock will be acquired by the other thread.

That way it is ensured that the code between lock.acquire() and lock.release() is executed in one thread at a time.

EDIT

As Gerhard pointed in the other answer, you can use the context management protocol with locks:

with lock:
    if set_this_var_only_once is None:
        set_this_var_only_once = "not None"

That will also make sure the lock is correctly released in case of an exception within the locked block.

Sign up to request clarification or add additional context in comments.

Comments

4

You need to lock the variable like this:

from threading import Lock

lock = Lock()
set_this_var_only_once = None

def worker():
    with lock:
        if set_this_var_only_once is None:
            set_this_var_only_once = "not None

2 Comments

Using with is a nice touch ;)
Makes the code more clear and it's practically identical with Java's synchronized blocks, i.e. synchronized(lock) { /*code*/ }.
1

No it's not.

If another thread gets control after the current thread has fetched the variable, it may fetch the variable, increment it, and write it back, before the current thread does the same thing. And since they’re both seeing the same original value, only one item will be accounted for.

Here's this good article about it.

P.S. Also the following line is needed in the worker():

global set_this_var_only_once 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.