1

我正在尝试制作一个哈希破坏应用程序,它将检查一个文件的所有行以及 rockyou 字典中的所有行。虽然对岩石进行预散列,但我有时间将一个散列检查到几秒钟,但仍然不够。这就是我将程序移至多线程的原因。但是我的线程停止了,没有出现任何异常。

import threading
import datetime

class ThreadClass(threading.Thread):
    hash_list=0
    def file_len(fname):
        with open(fname) as f:
            for i, l in enumerate(f):
                pass
        return i + 1
    list_len=file_len("list.txt")

    def run(self):
        while ThreadClass.list_len>0:
            ThreadClass.list_len=ThreadClass.list_len-1
            print str(threading.current_thread())+":"+str(ThreadClass.list_len)
for i in range(20):
    try:
        t = ThreadClass()
        t.start()
    except:
        raise

这是输出: 除了只有一个线程打印 当我在一段时间后运行它时,只有一个线程报告。为什么?感谢所有帮助

编辑:其中一个线程出现一个关键错误。我不知道那是什么

4

1 回答 1

4

由于计算哈希是 CPU 绑定的问题,因此由于 GIL,在 cPython 中使用多线程对您没有帮助。

如果有的话,您需要使用multiprocessing. 使用 a Pool,您的整个代码可以简化为:

import multiprocessing

def calculate(line):
    # ... calculate the hash ...
    return (line, 'calculated_result')

pool = multiprocessing.Pool(multiprocessing.cpu_count())

with open('input.txt') as inputfile:
    result = pool.map(calculate, inputfile)

print(result)
# compare results

至于您的线程问题:您同时ThreadClass.list_len从多个线程访问。首先访问它并将其与 0 进行比较。然后再次访问它,减少它并将其存储回来,这不是线程安全的 ,然后在打印时再次访问它。在任何这些操作之间,另一个线程可以修改该值。

为了证明这一点,我稍微修改了您的代码:

import threading
import datetime

lns = []
class ThreadClass(threading.Thread):
    hash_list=0
    list_len= 10000

    def run(self):
        while ThreadClass.list_len>0:
            ThreadClass.list_len=ThreadClass.list_len-1
            ln = ThreadClass.list_len        # copy for later use ...
            lns.append(ln)

threads = []
for i in range(20):
    t = ThreadClass()
    t.start()
    threads.append(t)

for t in threads:
    t.join()

print len(lns), len(set(lns)), min(lns)

当我运行 10 次时,我得到的是:

13473 9999 -1
10000 10000 0
10000 10000 0
12778 10002 -2
10140 10000 0
10000 10000 0
15579 10000 -1
10866 9996 0
10000 10000 0
10164 9999 -1

所以有时它似乎运行良好,但其他有很多值已被多次添加,并且 list_len 甚至设法得到负数。

如果你反汇编 run 方法,你会看到:

>>> dis.dis(ThreadClass.run)
 11           0 SETUP_LOOP              57 (to 60)
        >>    3 LOAD_GLOBAL              0 (ThreadClass)
              6 LOAD_ATTR                1 (list_len)
              9 LOAD_CONST               1 (0)
             12 COMPARE_OP               4 (>)
             15 POP_JUMP_IF_FALSE       59

 12          18 LOAD_GLOBAL              0 (ThreadClass)
             21 LOAD_ATTR                1 (list_len)
             24 LOAD_CONST               2 (1)
             27 BINARY_SUBTRACT     
             28 LOAD_GLOBAL              0 (ThreadClass)
             31 STORE_ATTR               1 (list_len)

 13          34 LOAD_GLOBAL              0 (ThreadClass)
             37 LOAD_ATTR                1 (list_len)
             40 STORE_FAST               1 (ln)

 14          43 LOAD_GLOBAL              2 (lns)
             46 LOAD_ATTR                3 (append)
             49 LOAD_FAST                1 (ln)
             52 CALL_FUNCTION            1
             55 POP_TOP             
             56 JUMP_ABSOLUTE            3
        >>   59 POP_BLOCK           
        >>   60 LOAD_CONST               0 (None)
             63 RETURN_VALUE    

简而言之,您可以说,在任何这些行之间,另一个线程可以运行并修改某些内容。要从多个线程安全地访问一个值,您需要同步访问。

例如使用threading.Lock代码可以这样修改:

class ThreadClass(threading.Thread):
    # ...
    lock = threading.Lock()

    def run(self):
        while True:
            with self.lock:
                # code accessing shared variables inside lock
                if ThreadClass.list_len <= 0:
                    return
                ThreadClass.list_len -= 1
                list_len = ThreadClass.list_len   # store for later use...
            # not accessing shared state, outside of lock

我不完全确定这是您问题的原因,但它可能是,特别是如果您还在运行方法中从输入文件中读取。

于 2013-07-07T16:27:41.187 回答