其他链接:
- OC学习笔记之多线程
- Qt学习笔记之–多线程
由于实际运用中开辟线程和进程 都是在for 循环中进行的,所以不能不考虑同时 并发的线程和进程的数量。所以在下面使用都 有控制并发数的方法。
由于Python 多线程GIL锁
的原因,导致cpu处理环节,实际上是串行执行的,导致在CPU处理密集型的 方法中,采用多线程
处理却要比单线程
处理慢。所以有必要引进 多进程
处理方式。
一、threading 启动多线程
多线程在CPU密集型操作中是串行的,多线程使用与I/O操作或者网络请求操作。
- 使用
Semaphore
控制并发线程数量 threading.Lock()
将子线程数据上锁
模拟代码
import threading
import os
def run_multi_thread():
def long_time_task_in_subThread(_index):
sem.acquire()
lock.acquire()
_dic["num"] += 1
print("num:"+str(_dic["num"]) + "\t" + str(threading.current_thread()))
lock.release()
sem.release()
_dic = {"num":0}
sem=threading.Semaphore(8) #控制最多 同时运行 8个线程
lock=threading.Lock() #将锁内的代码串行化
l = []
for i in range(50):
t=threading.Thread(target=long_time_task_in_subThread,args=(i,))
t.start()
l.append(t)
for t in l:
t.join()
print("---over")
if __name__=='__main__':
run_multi_thread()
运行结果
num:1 <Thread(Thread-1, started 25320)>
num:2 <Thread(Thread-2, started 25172)>
num:3 <Thread(Thread-3, started 19528)>
num:4 <Thread(Thread-4, started 21772)>
num:5 <Thread(Thread-5, started 12264)>
num:6 <Thread(Thread-6, started 8612)>
num:7 <Thread(Thread-7, started 18588)>
num:8 <Thread(Thread-8, started 15840)>
num:9 <Thread(Thread-9, started 25448)>
num:10 <Thread(Thread-10, started 22076)>
num:11 <Thread(Thread-11, started 20668)>
num:12 <Thread(Thread-12, started 10736)>
num:13 <Thread(Thread-13, started 24532)>
num:14 <Thread(Thread-14, started 13352)>
num:15 <Thread(Thread-15, started 15108)>
num:16 <Thread(Thread-16, started 23400)>
num:17 <Thread(Thread-17, started 20712)>
num:18 <Thread(Thread-18, started 17332)>
num:19 <Thread(Thread-19, started 23236)>
num:20 <Thread(Thread-20, started 24048)>
num:21 <Thread(Thread-21, started 24616)>
num:22 <Thread(Thread-22, started 24956)>
num:23 <Thread(Thread-23, started 25300)>
num:24 <Thread(Thread-24, started 18692)>
num:25 <Thread(Thread-25, started 15256)>
num:26 <Thread(Thread-26, started 24368)>
num:27 <Thread(Thread-27, started 22692)>
num:28 <Thread(Thread-28, started 16732)>
num:29 <Thread(Thread-29, started 22104)>
num:30 <Thread(Thread-30, started 23652)>
num:31 <Thread(Thread-31, started 25560)>
num:32 <Thread(Thread-32, started 25564)>
num:33 <Thread(Thread-33, started 25512)>
num:34 <Thread(Thread-34, started 25568)>
num:35 <Thread(Thread-35, started 23660)>
num:36 <Thread(Thread-36, started 25312)>
num:37 <Thread(Thread-37, started 22084)>
num:38 <Thread(Thread-38, started 25456)>
num:39 <Thread(Thread-39, started 25444)>
num:40 <Thread(Thread-40, started 24512)>
num:41 <Thread(Thread-41, started 25440)>
num:42 <Thread(Thread-42, started 25296)>
num:43 <Thread(Thread-43, started 24816)>
num:44 <Thread(Thread-44, started 25332)>
num:45 <Thread(Thread-45, started 23324)>
num:46 <Thread(Thread-46, started 22316)>
num:47 <Thread(Thread-47, started 7300)>
num:48 <Thread(Thread-48, started 23704)>
num:49 <Thread(Thread-49, started 22928)>
num:50 <Thread(Thread-50, started 11604)>
---over
[Finished in 0.3s]
二、Multiprocessing 的 Pool 启动多进程(控制并发数)
在CPU密集型操作中 多进程优化很明显
- 多进程使用
multiprocessing
的Pool
来控制进程数量,防止进程数量过多 崩溃。 - 使用
Manager
来进行共有数据 同步 ,并加锁 Pool().apply_async
进行 子线程调用
from multiprocessing import Pool
import os, time
import multiprocessing as mp
def long_time_task_in_subProcess(_dic, _lock1):
# 多进程处理中
#加入这里是 有 2-3M的字符串 多次查找其中的子字符串,能明显感觉多进程 比 多线程的优势。
time.sleep(0.01)
#防止修改有误,添加共享锁
_lock1.acquire()
_dic["num"] += 1
_c = _dic["num"]
_st = "时间:" + str(time.time())+ " num:"+str(_c) + " 进程ID:" + str(os.getpid())
_lock1.release()
return _st
def run_multi_process():
_cpuCount = mp.cpu_count()
m = mp.Manager()
# _dic 和 _lock1 需要在多进程中共享数据,方便修改。
_dic = m.dict()
_lock1 = m.Lock()
_dic["num"] = 0
results = []
p = mp.Pool(processes=_cpuCount*2)
for i in range(50):
results.append(p.apply_async(long_time_task_in_subProcess, args=(_dic, _lock1, )))
p.close()
p.join()
print("---over")
#如果dic 在子进程中进行打印,会发现 打印顺序是乱的,并不是按照时间顺序进行打印的,
#所以 这里将子进程的时间数据 返回后排序,进一步检测,是否是按照 _dic["num"] 的顺序来进行 +1的。
_list = [_result.get() for _result in results]
_list.sort()
for x in _list:
print(x)
if __name__=='__main__':
run_multi_process()
下面是运行结果,可见,最后确实是 num 加到了 50.
---over
---over
时间:1619684812.2807126 num:1 进程ID:5900
时间:1619684812.2817101 num:2 进程ID:10284
时间:1619684812.3116302 num:3 进程ID:21748
时间:1619684812.314622 num:4 进程ID:21444
时间:1619684812.3166168 num:5 进程ID:22472
时间:1619684812.3186111 num:6 进程ID:19680
时间:1619684812.3196087 num:7 进程ID:20152
时间:1619684812.3285844 num:8 进程ID:24156
时间:1619684812.3295817 num:9 进程ID:24404
时间:1619684812.3315783 num:10 进程ID:22448
时间:1619684812.3345685 num:11 进程ID:3112
时间:1619684812.3435445 num:12 进程ID:15376
时间:1619684812.3525202 num:13 进程ID:22192
时间:1619684812.3545163 num:14 进程ID:23164
时间:1619684812.3565097 num:15 进程ID:17076
时间:1619684812.3585036 num:16 进程ID:22272
时间:1619684812.362494 num:17 进程ID:21748
时间:1619684812.3754587 num:18 进程ID:10284
时间:1619684812.3754587 num:19 进程ID:4376
时间:1619684812.3774548 num:20 进程ID:21444
时间:1619684812.3784504 num:21 进程ID:19680
时间:1619684812.3794477 num:22 进程ID:24156
时间:1619684812.3804457 num:23 进程ID:5900
时间:1619684812.3814452 num:24 进程ID:3112
时间:1619684812.39042 num:25 进程ID:15376
时间:1619684812.3924136 num:26 进程ID:23164
时间:1619684812.3924136 num:27 进程ID:22472
时间:1619684812.393412 num:28 进程ID:22272
时间:1619684812.3944092 num:29 进程ID:20152
时间:1619684812.3954053 num:30 进程ID:22192
时间:1619684812.3964021 num:31 进程ID:24404
时间:1619684812.3974 num:32 进程ID:22448
时间:1619684812.4043822 num:33 进程ID:20616
时间:1619684812.4073744 num:34 进程ID:17076
时间:1619684812.4093685 num:35 进程ID:21748
时间:1619684812.4113638 num:36 进程ID:7884
时间:1619684812.4133577 num:37 进程ID:18860
时间:1619684812.4143555 num:38 进程ID:22388
时间:1619684812.417347 num:39 进程ID:24636
时间:1619684812.4243286 num:40 进程ID:21444
时间:1619684812.425326 num:41 进程ID:10284
时间:1619684812.4263227 num:42 进程ID:24156
时间:1619684812.4273195 num:43 进程ID:4376
时间:1619684812.4283168 num:44 进程ID:19680
时间:1619684812.4283168 num:45 进程ID:5900
时间:1619684812.4352984 num:46 进程ID:22272
时间:1619684812.436296 num:47 进程ID:22744
时间:1619684812.436296 num:48 进程ID:22448
时间:1619684812.4372928 num:49 进程ID:3112
时间:1619684812.4382904 num:50 进程ID:22472
[Finished in 1.4s]
三、Multiprocessing的Process 启动进程(不控制并发数)
- 使用
Process
启动进程
虽然代码简单, 但是不能控制进程并发数量,如果循环量过大,可能导致进程数量开辟过大。
from multiprocessing import Process
import os, time
def run_proc(name):
print("子进程ID: %s" % (os.getpid()))
if __name__ == '__main__':
print("主进程ID: %s." % os.getpid())
l = []
for x in range(1,10):
p = Process(target=run_proc, args=(x,))
p.start()
l.append(p)
for t in l:
t.join()
print("进程结束")
主进程ID: 6648.
子进程ID: 5668
子进程ID: 20124
子进程ID: 18352
子进程ID: 8104
子进程ID: 15384
子进程ID: 1644
子进程ID: 15840
子进程ID: 18316
子进程ID: 2224
进程结束
[Finished in 0.5s]