如何多线程(可控数量)历遍完字典?

2019-10-22 10:35:37 +08:00
 qazwsxkevin

有一个字典,原本穷举历遍的,

ResultDict = {xxx,xxx,...}

def ProcessID(caseID):
    doSomething(caseID)

for eachID in ResultDict:
    doSomething(eachID)
    pass

单线程运行太慢了,
想作改动,控制 doSomething(eachID),同时进行 5 个线程(以后可能不止 5 个)去历遍完 ResultDict,多线程的教学,和简单测试代码我简单学习,运行过,
这里有个我不懂的多线程解决算法是:如果严格控制同时只能 5 个,第 6 个开始介入,必须要等前面 1 个结束了才能开始开始,( doSomething(eachID)处理时间都是不可预计的,总而言之只能同时 5 个,逐个按顺序压入 doSomething 处理),是不是“栈”的概念?
这样的线程控制算法,我简单搜了一下网上,似乎没有现成例子,高手们能讲解一下如何做到吗?
感谢感谢~~

3366 次点击
所在节点    Python
18 条回复
star00
2019-10-22 10:42:17 +08:00
开 5 个线程,每个线程循环从一个线程安全的队列抛个值,做处理就 ok 了
Trim21
2019-10-22 10:45:42 +08:00
用的手机,懒得查 api 了,可能某些单词拼的是错的…

你可以用 multiprocess.pool 里面的 threadpool,开一个最大数量为 5 的线程池,然后用 pool.map (或者 mapstar ),去作用在 resultdict.items ()上面
Vegetable
2019-10-22 10:45:52 +08:00
先说方法,multiprocessing.Pool.map 方法可以满足你的需求,不过需要解决好多线程之间的通信问题,并且 windows 上好像不能使用 multiprocessing 多线程,只能用协程,就没有意义了
scukmh
2019-10-22 10:45:59 +08:00
doSomething 确定是 io 密集嘛?
qazwsxkevin
2019-10-22 11:01:41 +08:00
感谢大伙热心的回复,先挑一些疑问简单回帖了先,手上还有其它事在忙着 ^_^

@Vegetable,是在 Win 的环境下作业的,那么多了线程,历遍的效率总会有提高的吧?

@scukmh,磁盘读写么? 不多,doSomething 就是轮流去生成几个 10 几 kb 的文本,读几个 1MB,2MB 大小的文件,下载几个网页(这个就是要等爬虫的返回时间,不确定时间点就在这里)

doSomething 函数封装得很好,函数完全是独立的,不交叉到局外通信,while 到不完成不返回,超过重试次数就自己结束,有一个地方交叉,可能就是抛出异常的时候,会向"d:\error.log"写点东西,会担心同时多进程同时异常都往 error.log 写内容?
ClericPy
2019-10-22 11:04:08 +08:00
最简单的还是 multiprocessing.dummy 里面的线程池, 可以 map 也可以自己调度, 符合楼主说的只能 5 并发, 如果不是 IO 密集的, 把 dummy 去掉就是多进程...

from multiprocessing.dummy import Pool


def do(sth):
print(sth)
return sth


pool = Pool(5)
tasks = [i for i in range(20)]
result = pool.map(do, tasks)

print(result)

这个目前信息看起来不像是栈
kaiser1992
2019-10-22 11:08:46 +08:00
把字典转成 list,然后平分到多个线程中去执行,如果 dosomething 是 io 密集型,多线程效率肯定会提升
Vegetable
2019-10-22 11:20:22 +08:00
@qazwsxkevin windows 上的 multiprocessing 原理和 linux 不太一样,我搜了一下别人的文章,好像也可以用多进程,我刚接触 python 的时候被坑过后来没关注了,你可以自己了解一下怎么用。
不启动多个进程,也就无法绕过 GIL 锁,不能利用多个核心,只能加速 io 密集型操作,在 linux 上是肯定可以启动多个进程的,如果计算任务足够重的话,系统会将任务分配给多核计算,能够跑满 cpu。
zdt3476
2019-10-22 11:47:37 +08:00
既然楼上已经回答了,说过题外话。 遍历。。。
emmmbu
2019-10-22 11:51:24 +08:00
上亿数据我的做法 分段扔线程安全的队列 启动任意多个线程从队列取一段数据,消费完再取 这样各个线程不相互影响啊
qza1212
2019-10-22 14:28:55 +08:00
一般多线程用生产消费模型,临界区用队列就行

假如非要用你说的字典这种,我推荐用 hash 分桶
ps1aniuge
2019-10-22 14:42:53 +08:00
$hash = @{
a = 1
b = 2
#省略
}

$hash.GetEnumerator() | ForEach-Object -ThrottleLimit 2 -Parallel {
#系统需求=powershell 7 preview4
#你的代码
#-ThrottleLimit 2 为并发数
}
qazwsxkevin
2019-10-22 14:49:16 +08:00
@Vegetable,感谢解答~
@qza1212 ,是的,刚刚琢磨到,字典历遍方式似乎不太适合我这样放到线程队列里,正在琢磨其它办法。。。
@ps1aniuge 学习 ing...
ps1aniuge
2019-10-22 15:03:27 +08:00
1 这个 hash 是非线程安全的,只能读。
2 当然也有线程安全的 hash。
https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=netframework-4.8

3powershell 基于。net,多线程可以用多核。
Hopetree
2019-10-22 15:50:35 +08:00
你们可以去看看 Python 最好用的(非第三方)多线程类,这才是 Python 多线程的未来。就下面三行代码就可以实现你上面说到要求,就这么简单好用,而且这个多线程类是自带线程锁的,对于 I/O 密集型的线程操作,简直就是爽翻天。

```
from concurrent.futures import ThreadPoolExecutor

with ThreadPoolExecutor(max_workers=5) as pool:
pool.map(dosomething, ResultDict.keys())
```
robinlovemaggie
2019-10-22 15:58:31 +08:00
注意审题,强调要求“同时”的情况,楼上的所有回答都不及格吧~
qazwsxkevin
2019-10-22 16:27:28 +08:00
@robinlovemaggie 我这两天自己学习了一下多线程,还不够深入,尽管上面高手们都提到了解决方法,其实一次取 5 个,每次 5 个这样压入函数进行独立线程处理,等 5 个处理完,再下一批,也是可以的,起码比起单线程处理要好多了。。。

细想了一下,保持 5 个,出 1 个进 1 个的 FIFO 队列算法,太难了,我这里的情况可能要连整个处理函数的设计都要改。。。

加上现在的集合来源,是字典,我本来在 for 的历遍里面是有一些 continue 拐弯的,现在也甚是头疼怎么改。。。
xfriday
2019-10-22 20:05:48 +08:00
看楼主的需求是并行 doSomething 吧,可以开个 N(=5)个队列,然后遍历 map 往 N 个队列里 push,N 个线程从各自的队列里 pop 消费

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/611662

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX