前几天处理数据,发现只开一个进程实在是太慢了。于是借此机会刚好把多线程和多进程复习一下。我主要看的是B站的莫烦python两个系列。周大神(周沫凡先生)的讲解比纯看文字容易多了。且像其他文字版的教程,有点太跳跃了。感谢周大神的讲解~以下内容均是我根据他的视频的整理。
Multiprocessing 让你的多核计算机发挥真正潜力 (莫烦 Python 教程)_哔哩哔哩 (゜-゜)つロ 干杯~-bilibiliwww.bilibili.com一、多线程Multi-Threads
1、线程的基本统计
import
输出结果为
5
[<_MainThread(MainThread, started 21440)>, <Thread(Thread-4, started daemon 15276)>, <Heartbeat(Thread-5, started daemon 17844)>, <HistorySavingThread(IPythonHistorySavingThread, started 13528)>, <ParentPollerWindows(Thread-3, started daemon 21116)>]
<_MainThread(MainThread, started 21440)>
表示有5个已经active的threading,一个是mainthread,另外是thread-4、thread-5、thead-3、savingThread;正在运行的是主线程。
2、增加target和传参args
def
运行结果
This is an added thread, numbet is <Thread(Thread-9, started 13780)>.
3、join功能
(1)没有join的时候,同时出现多个线程的情况
import
运行结果是
T1 start
All done
T1 finishes
可以看到,没有等到T1全部结束、就提前运行了all done,最后才是T1 finish。其实多线程是同时在进行的线程任务;如果某些时候想等到所有线程都完成之后、再进行其他的语句,那么需要用到join。
(2)通过join强制所有的子线程结束后、再运行主线程
def
运行结果
T1 start
T1 finishes
All done
(3)多个线程
def
运行结果:如果没join,那么顺序比较乱:首先是T1开始、T2开始、T2结束、主线程结束、T1结束
T1 start
T2 start
T2 finishes
All done
T1 finishes
如果加上join
def
运行结果
T1 start
T2 start
T2 finishes
T1 finishes
All done
4、Queue
- 多线程没有返回值,需要把运算的结果放在队列Queue中;然后对每个线程的结果从队列中取出来queue.get()、放到主线程中继续运算。
import
运行结果
[[1, 1, 1], [4, 4, 4], [9, 9, 9], [16, 16, 16]]
5、多线程未必更快
- 多线程存在GIL(global interpret lock)。为了实现多线程功能,程序把线程锁住,然后锁住了之后,只有唯一一个线程运算。Python只能够让线程在同一时间运算一个东西。在不停切换,看起来是多线程的。但实际上不是。
import
运行结果
1999998000000
Normal : 0.1705458164215088
1999998000000
multi_threading : 0.14860320091247559
不用多线程是 0.1705秒;用了多线程仅仅是稍微快了一点。
6、lock锁
- 第一个线程处理好了,结果传递给第二个线程,那么需要“锁住“第一个线程。同时需要共享的内存shared memory。
- 如果不加任何lock:
# 不加任何处理,同时运行两个线程
运行结果
Job1 : 1
Job1 : 2
Job1 : 3
Job2 : 13Job1 :
Job2 : 24
Job2 : 34
Job2 : 44
Job2 : 54
Job2 : 64
Job2 : 74
Job2 : 84
Job2 : 94
Job2 : 104
14
Job1 : 105
Job1 : 106
Job1 : 107
Job1 : 108
Job1 : 109
Job1 : 110
这两个线程会同时运行,且打印还比较乱。如果我想先结束job1,再进行job2,那么使用lock——定义一个全局变量lock,同时在每个job中传入lock。从而两者运行就不会相互影响。
- 加上lock
def
运行结果
Job1 : 1
Job1 : 2
Job1 : 3
Job1 : 4
Job1 : 5
Job1 : 6
Job1 : 7
Job1 : 8
Job1 : 9
Job1 : 10
Job2 : 20
Job2 : 30
Job2 : 40
Job2 : 50
Job2 : 60
Job2 : 70
Job2 : 80
Job2 : 90
Job2 : 100
Job2 : 110
二、多进程Multi-Processing
- 注意:涉及到windows+multiprocessing,anaconda系列已经没法运行了(陷入死循环)。这里我选择在jupyter中写好了程序,然后放到.py文件中,最后通过cmd运行。
- multiprocessing和multithread的用法基本一致。
1、简单介绍
import
写好了,放到叫做code1.py的文件中,然后通过“win+R”进入cmd,通过cd 路径,进入存方code1.py的文件夹。然后通过python code1.py运行
如果不能够运行,那么说明是环境变量没有配置好。配置环境变量如下,把python.exe的绝对路径添加到环境变量路径中。
2、效率对比:多线程、多进程、普通程序
import
同样,存在code.py中,然后通过cmd运行。
normal时间最长,多线程、多进程存在优势。
3、pool进程池
- 把所有要运行的东西放到池子中,python自己解决进程分配。
import
4、apply_async
import
在cmd中运行,结果
5、共享内存:shared memory
- 传统上,共享一个变量,通过global定义全局变量,放到多线程中,就能够共享变量。
- 在多进程中,即便传入一个全局变量A,分给每一个CPU,但是它们想要交流A行不通(例如核1计算A+1、传给核2计算A×2、传给核3)。这里需要通过共享内存才能够实现。 1、mp.Value(),这个是共享的值,定义了这个值,需要声明它的格式。例如,mp.value('i',1)就是整数,mp.value('d',0.1)就是double型。就能够被每个核读取。 2、mp.Array(),和numpy.array有区别。这个是一维列表,不是多维的。声明数据格式,mp.Array('i', [1,2,3])(多维列表是错的mp.Array('i', [[1],[2],[3]])错错错)。
6、lock-进程锁
- 如果没有共享内存,那么出现互相争抢value
import
在cmd运行结果是
- 如果通过lock锁住,那么就能够避免争抢value
import
三、总结
以上是看了周大神的视频的整理。后续我自己也将多线程用到了数据清洗中,效率确实比傻傻地用一个主线程要快多了。
希望对大家有用~