为什么需要线程池呢?
设想一下,如果我们使用有任务就开启一个子线程处理,处理完成后,销毁子线程或等得子线程自然死亡,那么如果我们的任务所需时间比较短,但是任务数量比较多,那么更多的时间是花在线程的创建和结束上面,效率肯定就低了。
线程池的原理:
既然是线程池(Thread pool),其实名字很形象,就是把指定数量的可用子线程放进一个"池里",有任务时取出一个线程执行,任务执行完后,并不立即销毁线程,而是放进线程池中,等待接收下一个任务。这样内存和cpu的开销也比较小,并且我们可以控制线程的数量。
线程池的实现:
线程池有很多种实现方式,在python中,已经给我们提供了一个很好的实现方式:Queue-队列。因为python中Queue本身就是同步的,所以也就是线程安全的,所以我们可以放心的让多个线程共享一个Queue。
那么说到线程池,那么理应也得有一个任务池,任务池中存放着待执行的任务,各个线程到任务池中取任务执行,那么用Queue来实现任务池是最好不过的。
1.low版线程池
设计思路:运用队列queue
将线程类名放入队列中,执行一个就拿一个出来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import queue import threading class ThreadPool( object ): def __init__( self , max_num = 20 ): self .queue = queue.Queue(max_num) #创建队列,最大数为20 for i in range (max_num): self .queue.put(threading.Thread) #将类名放入队列中 def get_thread( self ): return self .queue.get() #从队列中取出类名 def add_thread( self ): self .queue.put(threading.Thread) #进类名放入队列中 def func(arg, p): #定义一个函数 print (arg) import time time.sleep( 2 ) p.add_thread() pool = ThreadPool( 10 ) #创建对象,并执行该类的构造方法,即将线程的类名放入队列中 for i in range ( 30 ): thread = pool.get_thread() #调用该对象的get_thread方法,取出类名 t = thread(target = func, args = (i, pool)) #创建对象,执行func,参数在args中 t.start() |
由于此方法要求使用者修改原函数,并在原函数里传参数,且调用方法也发生了改变,并且有空闲线程浪费资源,实际操作中并不方便,故设计了下一版线程池。
2.绝版线程池
设计思路:运用队列queue
a.队列里面放任务
b.线程一次次去取任务,线程一空闲就去取任务
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
import queue import threading import contextlib import time StopEvent = object() class ThreadPool(object): def __init__(self, max_num, max_task_num = None): if max_task_num: self.q = queue.Queue(max_task_num) else : self.q = queue.Queue() self.max_num = max_num self.cancel = False self.terminal = False self.generate_list = [] self.free_list = [] def run(self, func, args, callback=None): "" " 线程池执行一个任务 :param func: 任务函数 :param args: 任务函数所需参数 :param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数 1 、任务函数执行状态; 2 、任务函数返回值(默认为None,即:不执行回调函数) : return : 如果线程池已经终止,则返回True否则None "" " if self.cancel: return if len(self.free_list) == 0 and len(self.generate_list) < self.max_num: self.generate_thread() w = (func, args, callback,) self.q.put(w) def generate_thread(self): "" " 创建一个线程 "" " t = threading.Thread(target=self.call) t.start() def call(self): "" " 循环去获取任务函数并执行任务函数 "" " current_thread = threading.currentThread() self.generate_list.append(current_thread) event = self.q.get() while event != StopEvent: func, args, callback = event try : result = func(*args) success = True except Exception as e: success = False result = None if callback is not None: try : callback(success, result) except Exception as e: pass with self.worker_state(self.free_list, current_thread): if self.terminal: event = StopEvent else : event = self.q.get() else : self.generate_list.remove(current_thread) def close(self): "" " 执行完所有的任务后,所有线程停止 "" " self.cancel = True count = len(self.generate_list) while count: self.q.put(StopEvent) count -= 1 def terminate(self): "" " 无论是否还有任务,终止线程 "" " self.terminal = True while self.generate_list: self.q.put(StopEvent) self.q.queue.clear() @contextlib .contextmanager def worker_state(self, state_list, worker_thread): "" " 用于记录线程中正在等待的线程数 "" " state_list.append(worker_thread) try : yield finally : state_list.remove(worker_thread) # How to use pool = ThreadPool( 5 ) def callback(status, result): # status, execute action status # result, execute action return value pass def action(i): print(i) for i in range( 30 ): ret = pool.run(action, (i,), callback) time.sleep( 3 ) print(len(pool.generate_list), len(pool.free_list)) print(len(pool.generate_list), len(pool.free_list)) pool.close() # pool.terminate() |
总结
以上就是本文关于Python探索之自定义实现线程池的全部内容,希望对大家有所帮助。如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!
原文链接:http://www.cnblogs.com/tangtingmary/p/7729998.html