Home > Software design >  How to make method call in a class thread-safe in python?
How to make method call in a class thread-safe in python?

Time:09-05

I'm a java engineer, and I'm trying to find a way to make calling of method in class thread-safe as the code snippet shows below:

class A:
  def __init__(self, id):
    self.x = X(id)

  def switch(self, new_id):
    self.x = X(new_id) # switch to another object  

  def log(self, msg):
    self.x.log(msg)

a = A()

I'm wondering, if a is being used by multi-thread, and sometimes switch method is called, will log method suffer from NullPointerException where self.x could be None with chances?

Thanks!

CodePudding user response:

At no point in the program does self.x get set to None. If we take a look at the generated Python byte code for the switch function, we see that self.x is immediately replaced with the new X instance without being set to None:

# Create the new X instance
0  LOAD_GLOBAL              0 (X)
2  LOAD_FAST                1 (new_id)
4  CALL_FUNCTION            1
# Store the new self.x
6  LOAD_FAST                0 (self)
8  STORE_ATTR               1 (x)
# Return from the function
10 LOAD_CONST               0 (None)
12 RETURN_VALUE

To double-check that there won't be any NullPointerException-like error such as this:

AttributeError: 'NoneType' object has no attribute 'log'

I created a simple Python script that calls repeatedly the switch and log methods in two separate threads without acquiring any locks.

import threading
import random

class X:
    def __init__(self, id) -> None:
        self.id = id

    def log(self, msg):
        print(f'{self.id}: {msg}')

class A:
  def __init__(self, id):
    self.x = X(id)

  def switch(self, new_id):
    self.x = X(new_id) # switch to another object  

  def log(self, msg):
    self.x.log(msg)

a = A(0)

def switch_t():
    while True:
        a.switch(random.randint(0, 100))

def log_t():
    while True:
        a.log(random.randint(0, 100))

t1 = threading.Thread(target=switch_t)
t2 = threading.Thread(target=log_t)

t1.start()
t2.start()

I ran this code for two minutes and it never threw any errors.
So, as long as you don't require self.x to remain the same during the execution of log, in case you wanted to access self.x more than one time, your code is completely fine and you won't come across any NullPointerException-like error.

One more thing to point out, in a = A(), you forgot the constructor arguments.

  • Related