Python如何中断多个线程

0 投票
2 回答
1197 浏览
提问于 2025-04-18 15:24

我在读一本书《Violent Python》,里面有个例子是教我怎么用字典来破解zip文件的密码。我有两个问题。首先,书里说要用多线程来提高性能,但我测试了一下(我知道用time.time()来计时不是最好的方法),结果发现不使用多线程的情况下快了大约十二秒。这是因为启动线程需要时间吗?第二,如果我不使用线程的话,一旦找到正确的密码,我可以通过打印结果并输入exit(0)来立刻结束程序。有没有办法在使用线程的情况下也能做到这一点,也就是说一旦找到结果就能同时结束所有其他线程呢?

import zipfile
from threading import Thread
import time

def extractFile(z, password, starttime):
    try:
        z.extractall(pwd=password)
    except:
        pass
    else:
        z.close()
        print('PWD IS ' + password)
        print(str(time.time()-starttime))

def main():
    start = time.time()
    z = zipfile.ZipFile('test.zip')
    pwdfile = open('words.txt')
    pwds = pwdfile.read()
    pwdfile.close()
    for pwd in pwds.splitlines():
        t = Thread(target=extractFile, args=(z, pwd, start))
        t.start()
        #extractFile(z, pwd, start)
    print(str(time.time()-start))

if __name__ == '__main__':
    main()

2 个回答

1

这段代码运行得很慢,因为Python有一个叫做全局解释器锁的东西,这意味着在任何时候只能有一个线程在执行。这就导致了在Python中,多线程的代码比单线程的代码运行得更慢。如果你想要创建一个真正的多线程应用程序,就需要使用多进程模块

如果你想要跳出线程并获取返回值,可以使用os._exit(1)。首先,在文件的顶部导入os模块:

import os

然后,把你的extractFile函数改成使用os._exit(1)

def extractFile(z, password, starttime):
    try:
        z.extractall(pwd=password)
    except:
        pass
    else:
        z.close()
        print('PWD IS ' + password)
        print(str(time.time()-starttime))
        os._exit(1)
2

在CPython中,有个叫做全局解释器锁("GIL")的东西,它限制了同时只能有一个线程在执行Python的字节码。

所以在这个应用中,使用multiprocessing.Poolmap方法可能更好,因为每次尝试都是独立的,不会互相影响;

import multiprocessing
import zipfile

def tryfile(password):
    rv = passwd
    with zipfile.ZipFile('test.zip') as z:
        try:
            z.extractall(pwd=password)
        except:
            rv = None
    return rv

with open('words.txt') as pwdfile:
    data = pwdfile.read()
pwds = data.split()

p = multiprocessing.Pool()
results = p.map(tryfile, pwds)
results = [r for r in results if r is not None]

这段代码默认会启动和你电脑核心数一样多的进程。它会在这些进程中不断用不同的密码来运行tryfile(),直到密码列表pwds用完为止,然后收集结果并返回。最后的列表推导是为了去掉那些结果为None的项。

需要注意的是,这段代码可以改进一下,让它在找到密码后就停止map。那样的话,你可能需要用到map_async和一个共享变量。此外,最好只加载一次压缩文件,然后在多个进程中共享。

撰写回答