41

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?

Basel Shishani
  • 7,735
  • 6
  • 50
  • 67
Shane
  • 4,875
  • 12
  • 49
  • 87

5 Answers5

52

You can use Locks, RLocks, Semaphores, Conditions, Events and Queues.
And this article helped me a lot.
Check it out: Laurent Luce's Blog

aayoubi
  • 11,285
  • 3
  • 22
  • 20
32

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

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • 1
    Out of curiosity, what do you mean by "the instance field ... is thread safe"? – Eli Bendersky Nov 29 '11 at 11:44
  • 3
    @EliBendersky: I guess he means operations like `self.Counter = Value` are thread-safe. Check this article I just found: http://effbot.org/pyfaq/what-kinds-of-global-value-mutation-are-thread-safe.htm – Shane Nov 29 '11 at 11:50
  • 1
    Assigning a value is not thread-safe. Say you have self.Counter=1 followed by self.Counter=2 then x=self.Counter. Then x can get 1 or 2 depending on thread switching, which makes assigning a value not thread-safe. – Basel Shishani Oct 12 '13 at 10:13
  • 3
    What I said is that a *single* assignment is atomic (except for 64-bit types). See http://stackoverflow.com/questions/4756536/what-operations-in-java-are-considered-atomic – Aaron Digulla Oct 14 '13 at 07:12
  • How is increment a problem as the instance variable of class exists only within one thread. Is an instance variable shared across threads? – variable Nov 06 '19 at 04:07
  • @variable In the code in the question, the same instance is shared between threads. – Aaron Digulla Nov 27 '19 at 13:35
  • This is the information that I was looking for actually. People who don't know `C`, don't even think about this while mutexing every operation. Still, I'll put an uppercase comment line with this info but I'll remove mutexes from my code to improve performance. – Orhan G. Hafif Nov 19 '20 at 15:06
  • In your first sentence you link an SO answer for _Java_. How is this relevant for this question? – He3lixxx Aug 01 '22 at 18:06
15

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.

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
  • I think that only class variable is not thread safe. Instance variables should be thread safe unless the class instance is created globally, and passed to the thread. What do you think? – variable Nov 08 '19 at 03:40
  • @variable because instances can be passed to different thread, your point dosn't make sense. Things are or aren't thread safe regardless of their implementation. – Maxime de Pachtere Apr 21 '20 at 10:07
2

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.

MarredCheese
  • 17,541
  • 8
  • 92
  • 91
Jack Mason
  • 45
  • 1
  • 8
    Surely multiple threads can access the same instance of an object? – Harry Johnston Sep 05 '12 at 03:05
  • 1
    @HarryJohnston Be very careful with that. It can add quite a bit of complexity. You may have to do some form of locking. – Austin Henley Sep 25 '12 at 03:41
  • 4
    @AustinHenley: yes, that's the point. The OP wanted to know whether instance variables were thread-safe or not. – Harry Johnston Sep 25 '12 at 04:52
  • I think that only class variable is not thread safe. Instance variables should be thread safe unless the class instance is created globally, and passed to the thread. What do you think? – variable Nov 08 '19 at 03:41
1

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

John Velonis
  • 1,449
  • 1
  • 15
  • 13