线程池

简介

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# coding:utf-8

from concurrent.futures import ThreadPoolExecutor
import time,os
import threading

lock = threading.Lock()

def work(i): # 线程锁 不像进程锁需要以参数形式传入函数。直接全局就行。
# lock.acquire() # 线程锁 锁住函数work,使得work不能 被多个线程同时执行。有锁可以严格保证执行顺序。
print(i,'is',os.getpid()) # 说明 多线程在一个进程中进行 。
time.sleep(2)
# lock.release()
return 'result %s' % i


if __name__ == '__main__':
# 同步调用
print('同步调用',os.getpid())
t = ThreadPoolExecutor(2)
result = []
for i in range(8):
# submit().result()是同步调用,提交/调用一个任务,然后就在原地等着,等到该任务执行完毕拿到结果,再执行下一行代码。
# submit() 是异步调用,不在原地等着,直接执行下一行代码
# submit() 实际上起到了一个加锁的效果。
# result() 事实上起到了一个解锁的效果。
# 同步调用时,即便是把work()中的 加锁解锁 注释掉,运行过程是一样的。
t_result = t.submit(work,(i)).result() # submit 就是提交进线程池 ,直接执行了。多线程执行的函数传参,是tuple类型。
result.append(t_result)
print('***************\n')
for res in result:
print('~~~~~',res)

print('\n------------------------------------------------\n')

# 异步调用
print('异步调用', os.getpid())
t = ThreadPoolExecutor(4)
result = []
for i in range(8):
# submit().result()是同步调用,提交/调用一个任务,然后就在原地等着,等到该任务执行完毕拿到结果,再执行下一行代码。
# submit() 是异步调用,不在原地等着,直接执行下一行代码
# submit() 实际上起到了一个加锁的效果。
# result() 事实上起到了一个解锁的效果。
# 异步调用时,把work()中的 加锁解锁 注释掉,运行过程就不一样了,
# 因为两个线程抢着干活,work本身没锁,同时有两个线程干活,效率更高。
t_result = t.submit(work, (i))
result.append(t_result)
print('***************\n')
for res in result:
print('~~~~~', res.result())
# 也就是submit运行一个子线程 到此处打住,再回到submit运行下一个子线程。
# 注意:从 submit 运行到 result 处,主线程不会反复执行。
# print('***************\n') 只会输出一次,
# 重复 从 submit 运行到 result 处是子线程而言。

# 线程的结束一般依靠线程函数的自然结束;
# 也可以在线程函数中调用thread.exit(),
# 抛出SystemExit exception,达到退出线程的目的。


# 我们来分析一下为什么上面例子中 ,异步2线程 比同步2线程快了一倍。
# 首先我们要明白,锁是锁什么的,是锁住 submit-result 以外的代码运行的!!!submit-result 范围内代码能运行!!!
# 任务来的流水线其实都是一样的8个循环,work方法内也没有锁。
# 同步的例子中,来了一个任务,两人抢,一个人抢到了,要等work一秒钟运行完return了,
# 锁才解除,主线程的流水线才启动,循环到下一个数字,下一个人才能抢到,如此循环实际上同一时间还是只有一人干活。
# 异步的例子中,来了一个任务,两人抢,一个人抢到了,此时submit-result范围外的代码运行被锁住了,但是submit-result 范围内代码能运行,
# submit-result范围其中 已经包括了for i in range(8):循环和for res in result:直到print之前的部分。
# for i in range(8):循环根本就没有等待,一直在循环,
# 主线程早早的就循环完了两个循,把任务安排完,剩下的就是线程池中子线程不断的完成任务了。
# 限制速度的只是 线程池的线程数和work本身的运行时间,相当于满负荷运行。如果线程池中2线程改成4线程,时间还会减半!!