线程安全单例不起作用。蟒



我试着找到这个bug,在这篇文章中描述的,我找到了。

这是一个单例类,我给了StackOverflow从这篇文章

当我开始测试这个类时,我发现,这个程序失败了(就像第一个链接中描述的那样)…

这是导致bug的最小示例:

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:Python27python.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: %dn" % 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次导致bug。这个bug是当main while循环在Agent.py中不迭代或迭代时,但是complete_task事件从不设置(在100次启动的情况下少于1次)。

注。我试图使用这个单例模式,但bug仍然在这里…

有想法吗,为什么它是这样工作的?任何建议,我如何创建单实例,线程安全类来存储所有应用程序的工作数据?

<引用类>我的英语不是很好。对不起。

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

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,因此竞争条件永远不会触发(没有多线程实例化)。事实上,您的示例允许您在没有单例的情况下工作,因为您只显式地实例化了您的类一次。

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

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

8.10。Queue——一个同步队列类

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

是的。这是线程安全的,所以你可以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!

最新更新