Question

I have wrote two classes. The first class MySingletonTSafe is thread-safe because I use threading.Lock() to control how to call the method that creates the instance. But I'm not sure whether the second class MySingletonNTSafe is thread-safe. How could the sencond class be proved not thread-safe if it is?

I have found the thread Creating a singleton in python but it couldn't solve my puzzle.

The following is my code:

#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-


import threading
import Queue
import time
import random


class MySingletonTSafe(object):
    """implement Singleton pattern, thread-safe"""
    _instance = None
    _lock = threading.Lock()

    def __new__(cls):
        if MySingletonTSafe._instance is None:
            with MySingletonTSafe._lock:
                if MySingletonTSafe._instance is None:
                    MySingletonTSafe._instance = super(MySingletonTSafe, cls).__new__(cls)

        return MySingletonTSafe._instance

    def __init__(self):
        MySingletonTSafe._instance = self


class MySingletonNTSafe(object):
    """implement Singleton pattern, not-thread-safe"""
    _instance = None

    def __new__(cls):
        if MySingletonNTSafe._instance is None:
            # time.sleep(random.randint(1, 10))
            MySingletonNTSafe._instance = super(MySingletonNTSafe, cls).__new__(cls)

        return MySingletonNTSafe._instance

    def __init__(self):
        MySingletonNTSafe._instance = self


class Test(object):
    """Test whether the class MySingleton works"""
    def __init__(self, tnum=3):
        self.tnum = tnum
        self.queue_st = Queue.Queue()
        self.queue_nst = Queue.Queue()

    def run(self):
        threads_s = [threading.Thread(target=self._run_st) for i in xrange(self.tnum)]
        for t in threads_s:
            t.start()
            t.join()

        threads_ns = [threading.Thread(target=self._run_nst) for i in xrange(self.tnum)]
        for t in threads_ns:
            t.start()
            t.join()

    def _run_st(self):
        # thread-safe case
        obj = MySingletonTSafe()
        self.queue_st.put(obj)

    def _run_nst(self):
        # not-thread-safe case
        obj = MySingletonNTSafe()
        self.queue_nst.put(obj)


if __name__ == '__main__':
    test = Test(tnum=10)
    test.run()

    objs_st = []
    while not test.queue_st.empty():
        objs_st.append(test.queue_st.get())
    last = objs_st.pop()
    for obj in objs_st:
        if not last == obj:
            print('NOT IDENTICAL')
            break
        else:
            print('IDENTICAL')

    objs_nst = []
    while not test.queue_nst.empty():
        objs_nst.append(test.queue_nst.get())
    last = objs_nst.pop()
    for obj in objs_nst:
        if not last == obj:
            print('NOT IDENTICAL')
            break
        else:
            print('IDENTICAL')
Was it helpful?

Solution

In this case, you can just come up with an execution where MySingletonNTSafe does the wrong thing in a multi-threaded environment. Here's one:

  1. Thread 1 calls MySingtonNTSafe(). It sees that MySingletonNTSafe._instance is None, then gets interrupted.
  2. Thread 2 calls MySingletonNTSafe(). It sees that MySingletonNTSafe._instance is still None, creates an instance, assigns it to _instance, and returns it.
  3. Thread 1 resumes execution. It creates an instance, assigns it to _instance, and returns it.

At this point, we have two instances, and the class has failed. It is not threadsafe.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top