python多线程数据竞赛

时间:2015-09-21 13:03:59

标签: python multithreading race-condition

我在python 2.7上制作多线程系统。 基本上,它有3个线程和一个带有共享数据的单例类。

Scheme of application 红色箭头 - 调用;蓝色箭头 - 访问

每个线程都是文件中的单独类。 main.py文件导入工作和通信文件以及共享数据。然后主线程在一个线程中调用工作类并在另一个线程中调用通信。这里共享数据,因为只有一个单例实例,在工作类和通信类的构造函数中传递。

档案main.py

import communication
import Worker
import Data

app_data = Data.Instance()
#...........

SRV = communication.Server(app_data)
SRV.setDaemon(True)
SRV.start()

#...........

while True
    #...........
    # MUST BE locker.acquire()
    if condition1:
        if condition2:
            job = Worker(app_data, SRV.taskResultSendToSlaves, app_data.ip_table[app_data.cfg.MY_IP]['tasks'].pop())
            job.setDaemon(True)
            job.start()
    # MUST BE locker.release()

文件communication.py

class Server(threading.Thread):

    # .................

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

    def run(self):
        srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        srv.settimeout(self.data.cfg.TIMEOUT)
        srv.bind((self.my_addr, self.my_port))
        srv.listen(self.data.cfg.NUMBER_OF_CLIENTS)
        print "Start server"
        while True:
            # HANDLING MESSAGES FROM OTHER PC

    # .................

文件Worker.py

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

    def run(self):
        import thread
        self.data.complete_task.clear()
        tick_before = time.time()
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        startupinfo.wShowWindow = subprocess.SW_HIDE
        p = subprocess.Popen(self.data.cfg.PATH_INTERPRETER + " " + self.data.cfg.PATH_TASKS + self.taskname, startupinfo=startupinfo, shell=False, stdout=subprocess.PIPE)
        job_result, err = p.communicate()
        tick_after = time.time()
        work_time = tick_after - tick_before  
        # MUST BE locker.acquire()      
        self.data.task_table[self.taskname]['status'] = 'complete'
        self.data.task_table[self.taskname]['result'] = job_result
        self.data.task_table[self.taskname]['time'] = work_time
        # MUST BE locker.release()
        logging.debug("%s task is done" % self.taskname)
        tr = threading.Thread(target=self.sender, name="SENDER", args=(self.taskname, ))
        tr.setDaemon(True)
        tr.start()
        tr.join()
        logging.debug("%s task is sent" % self.taskname)
        self.data.complete_task.set()
        thread.exit()

Singletone.py

class Singleton:

    def __init__(self, decorated):
        self._decorated = decorated

    def Instance(self):
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

def __call__(self):
    raise TypeError('Singletons must be accessed through `Instance()`.')

def __instancecheck__(self, inst):
    return isinstance(inst, self._decorated)

data.py

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


@Singleton
class Data:

    def __init__(self):
        logging.basicConfig(format=u'%(filename)-10s[LINE:%(lineno)d] <%(funcName)-15s> # %(levelname)-8s [%(asctime)s]  %(message)s'.encode('cp1251', 'ignore'), level=logging.DEBUG, filename='mylog.log')
        logging.log(100, '='*120)
        self.cfg = Configurations()
        self.ip_table = self.getIPTable()
        self.task_table = self.getTaskTable()
        self.locker = threading.Lock()
        self.initialization = threading.Event()
        self.initialization.clear()
        self.identification = threading.Event()
        self.identification.clear()
        self.complete_task = threading.Event()
        self.complete_task.set()
        self.flag_of_close = False

    def __str__(self):
        return "\
        {0}\n\
        \n\
        {1}\n\
        \n\
        {2}\n\
        ".format(str(self.cfg), self.strIPTable(), self.strTaskTable())

    def strIPTable(self):
        #return str(self.ip_table)
        result = ["%s = %s" % (key, str(value)) for key, value in self.ip_table.items()]
        result.sort()
        return "\n\t\t".join(result)

    def strTaskTable(self):
        #return str(self.task_table)
        result = ["%s = %s" % (key, str(value)) for key, value in self.task_table.items()]
        result.sort()
        return "\n\t\t".join(result)

    def getIPTable(self):
        result = {}
        if self.cfg.IPS:
            result = dict((item.strip(), {'status': True, 'port': 8000, 'tasks': []}) for item in self.cfg.IPS.split(','))
            # result = dict((item.strip(), {'status': False, 'port': 8000, 'tasks': []}) for item in self.cfg.IPS.split(','))
        result[self.cfg.MY_IP] = {'status': True, 'port': 8000, 'tasks': []}
        return result

    def getTaskTable(self):
        result = {}
        if self.cfg.TASKS:
            result = dict((item.strip(), {'status': 'uncomplete', 'result': '', 'time': 0}) for item in self.cfg.TASKS.split(','))
        return result

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


if __name__ == '__main__':
    data = Data.Instance()
    print data

Singleton i从stackoverflow中被盗

启动此系统后,有时会进行数据竞争。工作时和主线程同时读取共享数据。我想我们需要一个线程。锁定在这里。然后我犯了一个错误,我把Lock对象放在共享数据中并用它来分隔访问。很快就明白了我的错误。

  

文件名已更改,部分代码已删除。

但是现在我不知道我必须把Lock对象放在哪里,每个线程都可以轻松地以正确的方式访问和使用它。你能给我建议吗?

  

我的英语不是很好,所以要宽容。我希望你理解我的问题......

App is running

P.S。

除此之外,我试图在类的构造函数中传递Lock()对象。我也有同样的麻烦。该应用程序已经出现在访问数据的某个地方。我无法确切地知道它在哪里。每次启动都可能以50%的概率降低应用程序。

  

Worker file   Main file

1 个答案:

答案 0 :(得分:0)

我发现了这个错误。 这是Singleton类,但我不知道如何解决它。