有一个字典,原本穷举历遍的,
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 处理),是不是“栈”的概念?
这样的线程控制算法,我简单搜了一下网上,似乎没有现成例子,高手们能讲解一下如何做到吗?
感谢感谢~~
1
star00 2019-10-22 10:42:17 +08:00
开 5 个线程,每个线程循环从一个线程安全的队列抛个值,做处理就 ok 了
|
2
Trim21 2019-10-22 10:45:42 +08:00 via Android
用的手机,懒得查 api 了,可能某些单词拼的是错的…
你可以用 multiprocess.pool 里面的 threadpool,开一个最大数量为 5 的线程池,然后用 pool.map (或者 mapstar ),去作用在 resultdict.items ()上面 |
3
Vegetable 2019-10-22 10:45:52 +08:00
先说方法,multiprocessing.Pool.map 方法可以满足你的需求,不过需要解决好多线程之间的通信问题,并且 windows 上好像不能使用 multiprocessing 多线程,只能用协程,就没有意义了
|
4
scukmh 2019-10-22 10:45:59 +08:00
doSomething 确定是 io 密集嘛?
|
5
qazwsxkevin OP 感谢大伙热心的回复,先挑一些疑问简单回帖了先,手上还有其它事在忙着 ^_^
@Vegetable,是在 Win 的环境下作业的,那么多了线程,历遍的效率总会有提高的吧? @scukmh,磁盘读写么? 不多,doSomething 就是轮流去生成几个 10 几 kb 的文本,读几个 1MB,2MB 大小的文件,下载几个网页(这个就是要等爬虫的返回时间,不确定时间点就在这里) doSomething 函数封装得很好,函数完全是独立的,不交叉到局外通信,while 到不完成不返回,超过重试次数就自己结束,有一个地方交叉,可能就是抛出异常的时候,会向"d:\error.log"写点东西,会担心同时多进程同时异常都往 error.log 写内容? |
6
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) 这个目前信息看起来不像是栈 |
7
kaiser1992 2019-10-22 11:08:46 +08:00
把字典转成 list,然后平分到多个线程中去执行,如果 dosomething 是 io 密集型,多线程效率肯定会提升
|
8
Vegetable 2019-10-22 11:20:22 +08:00
@qazwsxkevin windows 上的 multiprocessing 原理和 linux 不太一样,我搜了一下别人的文章,好像也可以用多进程,我刚接触 python 的时候被坑过后来没关注了,你可以自己了解一下怎么用。
不启动多个进程,也就无法绕过 GIL 锁,不能利用多个核心,只能加速 io 密集型操作,在 linux 上是肯定可以启动多个进程的,如果计算任务足够重的话,系统会将任务分配给多核计算,能够跑满 cpu。 |
9
zdt3476 2019-10-22 11:47:37 +08:00
既然楼上已经回答了,说过题外话。 遍历。。。
|
10
emmmbu 2019-10-22 11:51:24 +08:00
上亿数据我的做法 分段扔线程安全的队列 启动任意多个线程从队列取一段数据,消费完再取 这样各个线程不相互影响啊
|
11
qza1212 2019-10-22 14:28:55 +08:00
一般多线程用生产消费模型,临界区用队列就行
假如非要用你说的字典这种,我推荐用 hash 分桶 |
12
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 为并发数 } |
13
qazwsxkevin OP |
14
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,多线程可以用多核。 |
15
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()) ``` |
16
robinlovemaggie 2019-10-22 15:58:31 +08:00
注意审题,强调要求“同时”的情况,楼上的所有回答都不及格吧~
|
17
qazwsxkevin OP @robinlovemaggie 我这两天自己学习了一下多线程,还不够深入,尽管上面高手们都提到了解决方法,其实一次取 5 个,每次 5 个这样压入函数进行独立线程处理,等 5 个处理完,再下一批,也是可以的,起码比起单线程处理要好多了。。。
细想了一下,保持 5 个,出 1 个进 1 个的 FIFO 队列算法,太难了,我这里的情况可能要连整个处理函数的设计都要改。。。 加上现在的集合来源,是字典,我本来在 for 的历遍里面是有一些 continue 拐弯的,现在也甚是头疼怎么改。。。 |
18
xfriday 2019-10-22 20:05:48 +08:00
看楼主的需求是并行 doSomething 吧,可以开个 N(=5)个队列,然后遍历 map 往 N 个队列里 push,N 个线程从各自的队列里 pop 消费
|