Python线程和代码的问题一般

时间:2017-07-08 10:27:23

标签: python multithreading python-3.x python-multithreading

给出以下Python3代码,使用线程:

class main:
    def __init__(self):
        self.text = open(os.getcwd()+"/FileScanLogs.txt", "a+")
        self.hashlist = queue.Queue()
        self.filelist = queue.Queue()
        self.top = '/home/'
        for y in range(12):
            self.u = threading.Thread(target=self.md5hash)
            self.u.daemon = True
            self.u.start()
        for x in range(4):
            self.t = threading.Thread(target=self.threader)
            self.t.daemon = True
            self.t.start()
        main.body(self)

    def body(self):
        start = time.time()
        self.text.write("Time now is " + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + "\n")
        for root, dirs, files in os.walk(self.top):
            for f in files:
                path = os.path.join(root,f)
                self.filelist.put(path)
        self.t.join()
        self.u.join()
        self.text.write("Total time taken     : " + str(time.time() - start) + "\n")
        print("Log file is created as " + os.getcwd() + "/FileScanLogs.txt")

    def md5hash(self):
        while True:
            entry = self.filelist.get()
            //hashing//
            lists = finalhash + ',' + entry
            self.hashlist.put(lists)
            self.filelist.task_done()

    def compare(self, hashed, path):
        f = open(os.getcwd() + "/database.csv", 'r')
        for row in f:
            if row.split(':')[1] == hashed:
                print("Suspicious File!")
                print("Suspecfs: " + row.split(':')[2] + "File name : " + path)

    def threader(self):
        while True:
            item = self.hashlist.get()
            hashes = item.split(',')[0]
            path = item.split(',')[1]
            self.compare(hashes, path)
            self.hashlist.task_done()

main()

问题1:在def body(self)中,存在行self.text.write("Time now is ...")。此行不会出现在创建的日志文件中。

问题2:在def compare(self, hashed, path)中,存在一行打印“可疑文件!”每次发生哈希冲突时都会file path。这条线总是按顺序打印,因为4个线程t正在争夺谁将首先打印。为此,我想我需要知道如何让Python线程顺序运行print命令,而不是他们喜欢 - 如何?

问题3:在def body(self)中,存在行self.u.join()self.t.join()。根据我所知,命令join()是一个命令,等待线程在继续之前终止。线程都没有终止。

附加信息1:我正在编写多线程,因为我需要稍后将代码转换为多处理。

附加信息2:如果我在浏览过程中误解了代码中的任何命令/语法,请告诉我。

1 个答案:

答案 0 :(得分:1)

问题1 :您正在写入文件缓冲区 - 仅当缓冲区已满,文件句柄已关闭或您明确调用{{1}时,它才会刷新到实际文件在它上面(即flush()

问题2 :您要么要求您的代码并行执行(而且它不是,但我们会这样做)但是您丢失了执行顺序,或者你按顺序执行订单。如果你想运行多个线程,让它们一个接一个地执行是没有意义的,因为那样你就不会并行执行代码了,你也可以执行主线程中的所有代码。

如果您只想将输出控制到STDOUT,只要它不会干扰线程执行,您就可以捕获想要打印的内容并将其打印到互斥下(所以只有一个)线程在当时写入)甚至将其传回主线程并让它管理对STDOUT的访问。一个简单的互斥示例是:

self.text.flush()

这不会保持线程执行的顺序(也不应该尝试至少你想要击败并行执行的目的),但至少线程将输出他们的PRINT_MUTEX = threading.Lock() def compare(self, hashed, path): # never mind the inefficiency, we'll get to that later out = [] # hold our output buffer with open(os.getcwd() + "/database.csv", 'r') as f: for row in f: row = row.split(':') if row[1] == hashed: out.append("Suspicious File!") out.append("Suspecfs: " + row[2] + "File name : " + path) if out: with self.PRINT_MUTEX: # use a lock to print out the results print("\n".join(out)) 结果一次一个,而不是散布他们的结果。如果您希望主线程/进程控制STDOUT,特别是因为您想将其转换为多处理代码,请选中this answer

问题3 :您的线程永远不会退出,因为它们卡在compare循环中 - 直到您脱离线程,线程将继续运行。我不知道您构建代码的方式背后的原因,但是如果您尝试并行化文件列表(主线程),读取,哈希(md5hash线程)和比较(线程程序线程)你可能想要在没有更多文件时停止散列,并在没有更多散列时停止比较。要做到这一点,你不能真正使用while True,因为它在那里向其他听众发出信号' (如果他们被Queue.task_done()电话阻止了你的电话,那么你已经完成了队列修改。

您应该使用Queue.join()信号,但是如果您想保留它threading.Event,则只能创建一个特殊属性来表示队列结束,然后将其放入队列中没有什么可以处理的,然后让你的线程在遇到这个特殊属性时退出它们的循环。让我们首先解决你的代码中的一个大问题 - 你根本不存储对线程的引用,你用最后一个线程覆盖它,这样你就无法真正控制执行流程 - 而是将最后一个线程引用存储在变量中,将所有引用存储在列表中。此外,如果您要等待所有内容关闭,请不要使用守护程序线程:

queue.Queue

现在我们可以修改def __init__(self): self.text = open(os.getcwd()+"/FileScanLogs.txt", "a+") # consider os.path.join() self.hashlist = queue.Queue() self.filelist = queue.Queue() self.hashers = [] # hold the md5hash thread references self.comparators = [] # hold the threader thread references self.top = '/home/' for _ in range(12): # you might want to consider a ThreadPool instead t = threading.Thread(target=self.md5hash) t.start() self.hashers.append(t) for _ in range(4): t = threading.Thread(target=self.threader) t.start() self.comparators.append(t) main.body(self) 方法,以便将上述特殊值添加到队列的末尾,以便工作线程知道何时停止:

main.body()

因此我们需要修改工作线程,以便在遇到队列结束时退出:

QUEUE_CLOSE = object()  # a 'special' object to denote end-of-data in our queues

def body(self):
    start = time.time()
    self.text.write("Time:  " + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + "\n")
    for root, dirs, files in os.walk(self.top):
        for f in files:
            path = os.path.join(root, f)
            self.filelist.put(path)
    self.filelist.put(self.QUEUE_CLOSE)  # no more files, signal the end of the filelist
    for t in self.hashers:  # let's first wait for our hashing threads to exit
        t.join()
    # since we're not going to be receiving any new hashes, we can...
    self.hashlist.put(self.QUEUE_CLOSE)  # ... signal the end of the hashlist as well
    for t in self.comparators:  # let's wait for our comparator threads to exit
        t.join()
    self.text.write("Total: " + str(time.time() - start) + "\n")
    self.text.close()  # close the log file (this will also flush the previous content)
    print("Log file is created as " + os.getcwd() + "/FileScanLogs.txt")

现在如果你运行它,如果你的未列出的散列部分正常工作,一切都应该最终退出。

除了尴尬的设置之外,你绝对应该做的一件事是优化def md5hash(self): while self.filelist: entry = self.filelist.get() if entry is self.QUEUE_CLOSE: # end of queue encountered self.filelist.put(self.QUEUE_CLOSE) # put it back for the other threads break # break away the processing finalhash = whatever_is_your_hash_code(entry) lists = finalhash + ',' + entry self.hashlist.put(lists) def threader(self): while True: item = self.hashlist.get() if item is self.QUEUE_CLOSE: # end of queue encountered self.hashlist.put(self.QUEUE_CLOSE) # put it back for the other threads break # break away the queue hashes = item.split(',')[0] path = item.split(',')[1] self.compare(hashes, path) 方法 - 因为CSV文件在执行期间没有变化(如果确实如此,你应该在 - 处理它 - 内存)加载整个CSV并循环遍历每个文件&#39;你要比较的哈希是荒谬的。将整个CSV加载为main.compare() hash<=>whatever,然后在现场进行比较(即dict)。

最后,正如我上面提到的,你的游行时间,嗯,下雨 - 所有这一切都是徒劳的!由于可怕的GIL,这里没有任何线程并行执行(实际上,只有文件加载在一定程度上,但任何优势都可能被散列数据所花费的时间所阻碍)。它们确实作为一个独立的,诚实的上帝系统线程运行,但GIL确保一次只运行其中一个线程,因此这个代码(处理方式)很可能比在一个线程中运行所有内容时慢。这对多处理过程中的帮助不大,因为你无法共享本地实例状态(嗯,你可以检查this answer,但它只是一个主要的PITA而且大多数都是时间不值得经历麻烦)。