给出以下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:如果我在浏览过程中误解了代码中的任何命令/语法,请告诉我。
答案 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并循环遍历每个文件'你要比较的哈希是荒谬的。将整个CSV加载为main.compare()
hash<=>whatever
,然后在现场进行比较(即dict
)。
最后,正如我上面提到的,你的游行时间,嗯,下雨 - 所有这一切都是徒劳的!由于可怕的GIL,这里没有任何线程并行执行(实际上,只有文件加载在一定程度上,但任何优势都可能被散列数据所花费的时间所阻碍)。它们确实作为一个独立的,诚实的上帝系统线程运行,但GIL确保一次只运行其中一个线程,因此这个代码(处理方式)很可能比在一个线程中运行所有内容时慢。这对多处理过程中的帮助不大,因为你无法共享本地实例状态(嗯,你可以检查this answer,但它只是一个主要的PITA而且大多数都是时间不值得经历麻烦)。