线程安全的Singleton不起作用。蟒

时间:2015-10-07 18:03:37

标签: python multithreading thread-safety singleton

我试图找到这个post中描述的错误,我发现了它。

这是Singleton类,我从这个post

中提供了StackOverflow

当我开始测试那个课程时,我发现,该程序会崩溃(就像在第一个链接中描述的那样)......

这是最小的例子,导致错误:

Singleton.py

import threading


class Singleton(object):
    __singleton_lock = threading.Lock()
    __singleton_instance = None

    @classmethod
    def Instance(cls):
        if not cls.__singleton_instance:
            with cls.__singleton_lock:
                if not cls.__singleton_instance:
                    cls.__singleton_instance = cls()
        return cls.__singleton_instance

Data.py

#-*- coding: utf-8 -*-
from singletone import Singleton
import threading


class Data(Singleton):

    def __init__(self):
        self.task_table = {"Zadanie1.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie2.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie3.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie4.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie5.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie6.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie7.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie8.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie9.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie10.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie11.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie12.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie13.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie14.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie15.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie16.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie17.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie18.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie19.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie20.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie21.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie22.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie23.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie24.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie25.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie26.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie27.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie28.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie29.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie30.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie31.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie32.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie33.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie34.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie35.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie36.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie37.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie38.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie39.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie40.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie41.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie42.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie43.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie44.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie45.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie46.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie47.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie48.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie49.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie50.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie51.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie52.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie53.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie54.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie55.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie56.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie57.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie58.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie59.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie60.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie61.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie62.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie63.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie64.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie65.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie66.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie67.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie68.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie69.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie70.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie71.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie72.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie73.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie74.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie75.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie76.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie77.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie78.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie79.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie80.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie81.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie82.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie83.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie84.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie85.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie86.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie87.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie88.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie89.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie90.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie91.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie92.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie93.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie94.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie95.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie96.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie97.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie98.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie99.py": {'status': 'uncomplete', 'result': '', 'time': 0}, "Zadanie100.py": {'status': 'uncomplete', 'result': '', 'time': 0}}
        self.complete_task = threading.Event()
        self.complete_task.set()

    def getTotalCompleteTasks(self):
        result = 0
        for taskname in self.task_table.keys():
            if self.task_table[taskname]['status'] == 'complete':
                result += 1
        return result

Worker.py

#-*- coding: utf-8 -*-
import threading
import subprocess


class Worker(threading.Thread):

    def __init__(self, data, taskname):
        self.taskname = taskname
        self.data = data
        threading.Thread.__init__(self)

    def run(self):
        self.data.complete_task.clear()
        print u"Start executing %s" % self.taskname
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        startupinfo.wShowWindow = subprocess.SW_HIDE
        p = subprocess.Popen("C:\Python27\python.exe tasks100.5\\" + self.taskname, startupinfo=startupinfo, shell=False, stdout=subprocess.PIPE)
        job_result, err = p.communicate()
        print u"Stop executing %s" % self.taskname
        self.data.task_table[self.taskname]['status'] = 'complete'
        self.data.complete_task.set()

Agent.py

#-*- coding: utf-8 -*-
import communication_servers as cs
from worker import Worker
from data import Data
import time


app_data = Data.Instance()
SRV = cs.Server(app_data)
SRV.setDaemon(True)
SRV.start()

my_tasks = app_data.task_table.keys()[:]


while True:
    print u"Complete tasks: %d\n" % app_data.getTotalCompleteTasks()
    time.sleep(1)

    if my_tasks:
        if app_data.complete_task.is_set():
            job = Worker(app_data, my_tasks.pop())
            job.setDaemon(True)
            print u"Running"
            job.start()
        else:
            print u"Can't run"
        print u"Test string"

Communication_server.py

#-*- coding: utf-8 -*-
import threading
import time


class Server(threading.Thread):

    def __init__(self, data):
        threading.Thread.__init__(self)

    def run(self):
        while True:
            print('Server still working...\n')
            time.sleep(5)

像ZadanieXX.py这样的所有文件就在那里:

import time
i = 2
p = i**400
time.sleep(5)
print p

只有减少版本的实际代码。并且它导致了在30次启动的1例中的错误。主要的while循环im. Agent.py没有迭代或迭代,但是complete_task事件从未设置(在不到1次100次启动的情况下)。

  

P.S。   我尝试使用this Singleton模式,但bug仍在这里......

任何想法,为什么以这种方式工作?任何建议,如何创建单实例,线程安全类来存储所有应用程序工作数据?

  

我的英语不是很好。遗憾。

2 个答案:

答案 0 :(得分:2)

请注意,使用锁定实际上不是线程安全的。在检查实例是否存在以及锁定对实例的访问权之间存在竞争条件:

def Instance(cls):
    if not cls.__singleton_instance:
        # another thread may succeed with the previous check again here
        with cls.__singleton_lock:
            if not cls.__singleton_instance:
                cls.__singleton_instance = cls()
    return cls.__singleton_instance

你应该交换锁的获取和__singleton_instance的检查,因为第一次守卫后来。

请注意,在最小的工作示例中,您将显式构造Singleton,因此竞争条件永远不会触发(没有多线程实例化)。实际上,您的示例允许您在没有单例的情况下工作,因为您显式只对您的类进行一次实例化。

答案 1 :(得分:1)

很抱歉这不是关于Singleton DP的答案,但我认为你没有必要使用Singleton。

在您的示例中,Data类仅用作数据存储,您可以使用python自己的模块Queue进行单个数据存储。

  

8.10. Queue - A synchronized queue class

     

Queue模块实现多生产者,多消费者队列。当必须在多个线程之间安全地交换信息时,它在线程编程中特别有用。此模块中的Queue类实现了所有必需的锁定语义。这取决于Python中线程支持的可用性;参见线程模块。

是。它是线程安全的,因此您可以Queue作为单个,居中的数据存储。

检查这样的事情。

#-*- coding: utf-8 -*-
from Queue import Queue
from threading import Thread
import time

# Set up some global variables
num_threads = 2
data_queue = Queue()

# Your Data
files = ['Zadanie1.py', 'Zadanie2.py', ... ]

def doSomething(i, q):
    while True:
        print '[{}] Waiting...'.format(i)
        filename = q.get()
        print '[{}] Working: {} '.format(i, filename)
        # Do something you need
        time.sleep(i + random.randint(1, 5))
        q.task_done()

# Set up threads
for i in range(num_threads):
    worker = Thread(target=doSomething, args=(i, data_queue,))
    worker.setDaemon(True)
    worker.start()
    # Note that you can start threads before filling queue,
    # because queue.get() will block until queue has data to return.

# Fill queue
for filename in files:
    print 'Queuing: {}'.format(filename)
    data_queue.put(filename)

# Wait for the queue to be empty.
data_queue.join()
print 'Done!'

结果将是这样的。

[0] Waiting...
[1] Waiting...
Queuing: Zadanie1.py
Queuing: Zadanie2.py
...
[1] Working: Zadanie1.py 
[0] Working: Zadanie2.py 
...
[0] Waiting...
[1] Waiting...
Done!

希望这有用!。