版本一:简易版线程池
线程池分析:1.一个容器(最大个数) 2.取一个少一个 3.容器中没有了会阻塞等待 4.线程执行完毕交还给线程
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import Queue
import threading
class ThreadPool(object):
def __init__(self, max_num=20):
self.queue = Queue.Queue(max_num)
for i in xrange(max_num):
self.queue.put(threading.Thread)
def get_thread(self):
return self.queue.get()
def add_thread(self):
self.queue.put(threading.Thread)
pool = ThreadPool(10)
def func(arg, p):
print arg
import time
time.sleep(2)
p.add_thread()
for i in xrange(30):
thread = pool.get_thread()
t = thread(target=func, args=(i, pool))
t.start()
缺点:线程没有被重用,被垃圾回收回收
版本二:复杂点
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import queue
import threading
import contextlib
import time
StopEvent = object() # object() 是所有实例的方法,空方法
class ThreadPool():
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:
# 判断已经创建的线程是否小于最大线程数,空闲线程个数是不是等于0
self.generate_thread() # 空闲线程数=0,小于最大线程数,就创建新线程
w = (func, args, callback,) # 上述不满足就把任务放进队列,把3个参数当做一个元组传进队列
self.q.put(w)
def generate_thread(self):
"""
创建一个线程
"""
t = threading.Thread(target=self.call) # 创建线程,执行call()函数
t.start()
def call(self):
"""
循环去获取任务函数并执行任务函数
"""
current_thread = threading.currentThread() # 获取当前线程名称
self.generate_list.append(current_thread) # 把当前线程名称放入已经创建线程列表,统计已经创建多少线程
event = self.q.get() # 去队列取任务
while event != StopEvent:
func, arguments, callback = event # 变量解包
try:
result = func(*arguments) # 执行action函数
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) # 如果队列中的任务等于空函数StopEvent就把当前的线程移除掉
@contextlib.contextmanager # 有了此装饰器就能使用with self.worker_state()执行上下文切换
def worker_state(self, state_list, worker_thread):
"""
用于记录线程中正在等待的线程数
"""
state_list.append(worker_thread)
try:
yield # 执行到这时,会跳出此函数,去执行with self.worker_state()函数,执行完毕时,再跳回此函数继续执行下面的代码
finally:
state_list.remove(worker_thread)
def close(self):
"""
执行完所有的任务后,所有线程停止
"""
self.cancel = True
full_size = len(self.generate_list) # 以创建线程有多少就创建多少个空函数
while full_size: # 然后把空函数放进队列中,让每一个线程取到任务
self.q.put(StopEvent)
full_size -= 1
def terminate(self):
"""
无论是否还有任务,终止线程
"""
self.terminal = True
while self.generate_list:
self.q.put(StopEvent)
self.q.queue.clear() # 把队列都清空
# 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): # 创建30个任务
ret = pool.run(action, (i,), callback) # 3个参数,函数名,元组,函数名
time.sleep(5)
print(len(pool.generate_list), len(pool.free_list))
print(len(pool.generate_list), len(pool.free_list))
# pool.close()
# pool.terminate()
更多参见:twisted.python.threadpool
上下文管理:https://docs.python.org/2/library/contextlib.html
上下文实例1
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import contextlib
@contextlib.contextmanager
def worker_state(state_list, worker_thread):
"""
用于记录线程中正在等待的线程数
"""
state_list.append(worker_thread)
try:
yield
finally:
state_list.remove(worker_thread)
free_list = []
current_thread = 'python'
with worker_state(free_list, current_thread): # 如果上述执行完了,则这个线程就闲了
print('redhat')
print('centos')
上下文实例2
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import contextlib
import socket
@contextlib.contextmanager
def context_socket(host,port):
sk = socket.socket()
sk.bind((host,port))
sk.listen(5)
try:
yield sk
finally:
sk.close()
with context_socket('127.0.0.1',8888) as sock: # 如果上述执行完了,则这个线程就闲了
print(sock)