python线程、线程池
一、Python线程
Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。
1、创建线程
1.1 自定义线程
# 自定义线程 import threading import time # 创建一个线程,继承threading.Thread class MyThread(threading.Thread): def __init__(self, num): threading.Thread.__init__(self) self.num = num # 每个线程执行的函数(任务) def run(self): print('running on number:%s ' % self.num) time.sleep(3) if __name__ == '__main__': for i in range(3): t1 = MyThread(i + 10) t2 = MyThread(i + 100) t1.start() t2.start() #----------- 输出结果--------------- running on number:10 running on number:100 running on number:11 running on number:101 running on number:12 running on number:102
1.2 普通创建
import threading import time # 全局变量 gl_num = 0 def show(arg): global gl_num time.sleep(1) gl_num += 1 print(gl_num) for i in range(3): t = threading.Thread(target=show, args=(i, )) t.start() print('main thread stop')
上述代码创建了3个“前台”线程,然后控制器就交给了CPU,CPU根据指定算法进行调度,分片执行指令。
更多方法:
- start 线程准备就绪,等待CPU调度
- setName 为线程设置名称
- getName 获取线程名称
- setDaemon 设置为后台线程或前台线程(默认)
如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止
如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止 - join 逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
- run 线程被cpu调度后自动执行线程对象的run方法
2、线程锁(Lock、RLock)
由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁 - 同一时刻允许一个线程执行操作。
#!/usr/bin/env python # -*- coding:utf-8 -*- import threading import time gl_num = 0 def show(arg): global gl_num time.sleep(1) gl_num +=1 print gl_num for i in range(10): t = threading.Thread(target=show, args=(i,)) t.start() print 'main thread stop'
Python多线程同步Lock、RLock、Semaphore、Event实例
Lock & RLock 用来确保多线程多共享资源的访问
Semaphore用来确保一定资源多线程访问时的上限
Event是最简单的线程间通信的方式
一、多线程同步 由于CPython的python解释器在单线程模式下执行,所以导致python的多线程在很多的时候并不能很好地发挥多核cpu的资源。大部分情况都推荐使用多进程。 python的多线程的同步与其他语言基本相同,主要包含: Lock & RLock :用来确保多线程多共享资源的访问。 Semaphore : 用来确保一定资源多线程访问时的上限,例如资源池。 Event : 是最简单的线程间通信的方式,一个线程可以发送信号,其他的线程接收到信号后执行操作。 二、实例 1)Lock & RLock Lock对象的状态可以为locked和unlocked 使用acquire()设置为locked状态; 使用release()设置为unlocked状态。 如果当前的状态为unlocked,则acquire()会将状态改为locked然后立即返回。当状态为locked的时候,acquire()将被阻塞直到另一个线程中调用release()来将状态改为unlocked,然后acquire()才可以再次将状态置为locked。 Lock.acquire(blocking=True, timeout=-1),blocking参数表示是否阻塞当前线程等待,timeout表示阻塞时的等待时间 。如果成功地获得lock,则acquire()函数返回True,否则返回False,timeout超时时如果还没有获得lock仍然返回False。 RLock与Lock的区别是:RLock中除了状态locked和unlocked外还记录了当前lock的owner和递归层数,使得RLock可以被同一个线程多次acquire()。
RLock 锁实例
# lock = threading.RLock() # lock = threading.Lock() # 互斥锁,同一时刻只允许一个线程执行操作 import threading import time # 设置一个全局变量 gl_num = 0 # lock = threading.Lock() # 可以归执行锁操作,一般使用该锁 lock = threading.RLock() def func(): # 加锁 lock.acquire() # 函数里要想修改全局变量,需要先声明 global gl_num gl_num += 1 time.sleep(1) print(gl_num) # 释放锁 lock.release() for i in range(3): t = threading.Thread(target=func) t.start() print('main thread stop')
信号量(Semaphore)
Semaphore管理一个内置的计数器,每当调用acquire()时内置计数器-1;调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
Semaphore实例:(允许同一时间有五个线程同时运行)
# 信号量(semaphore) import time, threading def run(n): # 信号量,加锁 semaphore.acquire() time.sleep(1) print('run the thread: %s' % n) # 信号量,释放 semaphore.release() if __name__ == '__main__': num = 0 # 创建信号量,并且最多允许5个线程同时运行 semaphore = threading.BoundedSemaphore(5) for i in range(20): t = threading.Thread(target=run, args=(i, )) t.start() print('main thread stop')
事件(event)
python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。
Event内部包含了一个标志位,初始的时候为false。
可以使用使用set()来将其设置为true;
或者使用clear()将其从新设置为false;
可以使用is_set()来检查标志位的状态;
另一个最重要的函数就是wait(timeout=None),用来阻塞当前线程,直到event的内部标志位被设置为true或者timeout超时。如果内部标志位为true则wait()函数理解返回。
event 实例:(当inp为true时才会执行print('execute'))
# 事件(event) import threading def do(event): print('start') event.wait() print('execute') # 创建event对象 event_obj = threading.Event() for i in range(2): t = threading.Thread(target=do, args=(event_obj, )) t.start() event_obj.clear() inp = input('>>>>>>:') if inp == 'true': event_obj.set() ----------输出结果---------- start start >>>>>>:true execute execute
条件(Condition)
使得线程等待,只有满足某条件时,才释放n个线程
Condition 实例
# 条件(condition) import threading def run(n): con.acquire() con.wait() print('run the thread: %s' % n) con.release() if __name__ == '__main__': con = threading.Condition() for i in range(10): t = threading.Thread(target=run, args=(i, )) t.start() while True: inp = input('>>>>>>: ').strip() if inp == 'q': break con.acquire() con.notify(int(inp)) con.release() # --------输出结果-------- >>>>>>: 1 >>>>>>: run the thread: 0 2 >>>>>>: run the thread: 2 run the thread: 1
wait_for()
import threading def condition_func(): ret = False inp = input('>>>>>>') if inp == '1': ret = True return ret def run(n): con.acquire() con.wait_for(condition_func) print('run the thread: %s' % n) con.release() if __name__ == '__main__': con = threading.Condition() for i in range(10): t = threading.Thread(target=run, args=(i, )) t.start()
定时器 (Timer )
定时器,指定n秒后执行某操作
Timer 实例
# Timer定时器 from threading import Timer def hello(): print('hello, world') def hi(): print('hi, world') t = Timer(1, hello) t1 = Timer(2, hi) t.start() t1.start() -------输出结果--------- hello, world hi, world
线程池
1、创建线程池
# 自定义线程池
import threading import queue import time class ThreadPool: def __init__(self, max_num=20): self.queues = queue.Queue(max_num) for i in range(max_num): self.queues.put(threading.Thread) def get_thread(self): return self.queues.get() def add_thread(self): self.queues.put(threading.Thread)
# 设定线程池 最大线程数 pool = ThreadPool(5) def func(arg, p): print(arg) time.sleep(1) pool.add_thread() for i in range(30): thread = pool.get_thread() t = thread(target=func, args=(i, pool)) t.start() print('main thread stop')
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, arguments, callback = event try: result = func(*arguments) 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 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.empty() @contextlib.contextmanager def worker_state(self, state_list, worker_thread): """ 用于记录线程中正在等待的线程数 """ state_list.append(worker_thread) try: yield finally: state_list.remove(worker_thread) pool = ThreadPool(5) def callback(status, result): pass def action(i): print(i) for i in range(30): ret = pool.run(action, (i,), callback) 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()
二、Python 进程