Shane
Shane

Reputation: 4983

Are Python instance variables thread-safe?

OK, check following codes first:

class DemoClass():

    def __init__(self):
        #### I really want to know if self.Counter is thread-safe. 
        self.Counter = 0

    def Increase(self):
        self.Counter = self.Counter + 1

    def Decrease(self):
        self.Counter = self.Counter - 1

    def DoThis(self):
        while True:
            Do something

            if A happens:
                self.Increase()
            else:
                self.Decrease()

            time.sleep(randomSecs)

    def DoThat(self):
        while True:
            Do other things

            if B happens:
                self.Increase()
            else:
                self.Decrease()

            time.sleep(randomSecs)

    def ThreadSafeOrNot(self):
        InterestingThreadA = threading.Thread(target = self.DoThis, args = ())
        InterestingThreadA.start()

        InterestingThreadB = threading.Thread(target = self.DoThat, args = ())
        InterestingThreadB.start()

I'm facing same situation as above. I really want to know if it's thread-safe for self.Counter, well if not, what options do I have? I can only think of threading.RLock() to lock this resource, any better idea?

Upvotes: 44

Views: 81317

Answers (5)

aayoubi
aayoubi

Reputation: 12069

Variables accessed by multiple threads at the same time will lead to race conditions and unwanted behaviours. You must rely on Locks, RLocks, Semaphores, Conditions, Events or Queues.

The following article helps to understand the difference between each and when to use them in a multi-threaded environment:

Upvotes: 50

Aaron Digulla
Aaron Digulla

Reputation: 328594

Using the instance field self.Counter is thread safe or "atomic" (or here). Reading it or assigning a single value - even when it needs 4 bytes in memory, you will never get a half-changed value. But the operation self.Counter = self.Counter + 1 is not because it reads the value and then writes it - another thread could change the value of the field after it has been read and before it is written back.

So you need to protect the whole operation with a lock.

Since method body is basically the whole operation, you can use a decorator to do this. See this answer for an example: https://stackoverflow.com/a/490090/34088

Upvotes: 32

John Velonis
John Velonis

Reputation: 1649

The Atomos library provides atomic (thread-safe) wrappers for Python primitives and objects, including atomic counters. It uses single-writer/multiple-reader locks.

Upvotes: 1

Jack Mason
Jack Mason

Reputation: 45

self.Counter is an instance variable, so each thread has a copy.

If you declare the variable outside of __init__(), it will be a class variable. All instances of the class will share that instance.

Upvotes: 2

Eli Bendersky
Eli Bendersky

Reputation: 273436

No, it is not thread safe - the two threads are essentially modifying the same variable simultaneously. And yes, the solution is one of the locking mechanisms in the threading module.

BTW, self.Counter is an instance variable, not a class variable.

Upvotes: 16

Related Questions