线程
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
threading模块
python的标准库提供了两个模块用于多线程处理,_thread和threading,_thread是低级模块,threading是高级模块,是对_thread进行了封装。
启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行:
线程有两种调用方式:直接调用和继承式调用
直接调用
import time import threading def sayhi(num): # 定义每隔线程都要运行的函数 print('%s is say hi' %num) time.sleep(3) if __name__ == '__main__': t1 = threading.Thread(target = sayhi, args = [1, ]) # 调用Thread方法生成一个线程实例,第一个参数tartget表示进程要执行的函数,args表示要传递给进程函数的参数 t2 = threading.Thread(target = sayhi, args = [2, ]) t1.start() # 启动线程 t2.start() t1.join() # 等待子线程完毕,这句话的意思就等待一个线程执行完在执行这句话后面的逻辑,join方法还可以接收一个超时时间参数,表示最多等待多长时间,超过这个时间就不等了,继续执行下面的语句,注意,是不等待,不是中断线程的执行 t2.join() print(t1.getName()) # getName()表示获取线程的名称,默认Thread-1、Thread-2...这种命名方式 print(t2.getName())
继承式调用
import threading
import time
class Mythreading(threading.Thread): ''' 定义一个类,继承自threading.Thread ''' def __init__(self, num): ''' 初始化方法 :param num: :return: ''' threading.Thread.__init__(self) self.num = num def run(self): ''' 重写run方法,也就是每个线程要执行的函数 :return: ''' print('%s is say hi' %self.num) time.sleep(5) if __name__ == '__main__': t1 = Mythreading(1) # 用刚才定义的类创建进程对象 t2 = Mythreading(2) t1.start() t2.start() t1.join() t2.join() print(t1.getName()) print(t2.getName())
守护线程
守护线程的方式,是的主线程执行完毕强制结束下面的子线程的运行。
在python中,主线程和子线程是并行执行的,主线程会等待子线程执行完毕后再退出(主线程在启动子线程后会继续向下执行,并不等待子线程)。
import time import threading def child(n): ''' 子线程执行的函数 :param n: :return: ''' print('[%s]------running---- ' % n) time.sleep(2) print('--done--') def main(): ''' 主线程要执行的函数 :return: ''' for i in range(2): # 循环生成2个子线程 t = threading.Thread(target=child,args=[i,]) t.start() print('starting thread', t.getName()) m = threading.Thread(target=main,args=[]) # 创建主线程对象 m.setDaemon(True) #将主线程设置为Daemon线程,它退出时,其它子线程会同时退出,不管是否执行完任务 m.start() # 启动主线程 m.join() # 阻塞等待主线程执行完毕,这里不起作用
#time.sleep(3) print("---main thread done----")
线程方法
start 线程准备就绪,等待CPU调度
setName 为线程设置名称
getName 获取线程名称
setDaemon 设置为后台线程或前台线程(默认)
如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止
如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
join 逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
run 线程被cpu调度后自动执行线程对象的run方法
线程锁(互斥锁Mutex)
import threading import time def addNum(): global num # 调用全局变量num print('--get num:', num) time.sleep(1) lock.acquire() #申请锁 num += 1 # 每个线程都对num进行加1操作 lock.release() #释放锁 print(num) if __name__ == '__main__': lock = threading.Lock() num = 0 thread_list = [] # 初始化一个线程列表 for i in range(10000): # 循环启动10000个进程 t = threading.Thread(target = addNum) t.start() thread_list.append(t) # 加入到线程列表中 for t in thread_list: # 循环等待线程列表里的所有线程结束 t.join() print(num) # 打印num的最终值
python3.x已经修复了这个问题,不加锁结果也是正确的
RLock 递归锁
当一个大锁中还要再包含子锁的时候,如果再用threading.Lock的话,程序锁和钥匙会出现对不上的情况,这时候就需要用到递归锁
import threading,time def run1(): print("grab the first part data") lock.acquire() global num num +=1 lock.release() return num def run2(): print("grab the second part data") lock.acquire() global num2 num2+=1 lock.release() return num2 def run3(): lock.acquire() res = run1() print('--------between run1 and run2-----') res2 = run2() lock.release() print(res,res2) if __name__ == '__main__': num,num2 = 0,0 lock = threading.RLock() for i in range(10): t = threading.Thread(target=run3) t.start() while threading.active_count() != 1: print(threading.active_count()) else: print('----all threads done---') print(num,num2)
信号量(Semaphore)
刚才说的锁也是互斥锁,同时只能有一个线程操作,而Semaphore可以同时允许一定数量的线程更改数据。
import threading,time def run(n): semaphore.acquire() time.sleep(1) print("run the thread: %s " %n) semaphore.release() if __name__ == '__main__': num= 0 semaphore = threading.BoundedSemaphore(5) #最多允许5个线程同时运行 for i in range(20): t = threading.Thread(target=run,args=(i,)) t.start() while threading.active_count() != 1: pass #print threading.active_count() else: print('----all threads done---') print(num)
Events
事件可以理解为就是一个信号,他只有两个状态可以理解为真和假,常用方法如下
set():相当于设置为真
clear():相当于设置为假
isSet():判断是否为真
wait():等待事件置为真(阻塞)
通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。
import threading def light(): ''' 信号灯进程的线程执行的函数 :return: ''' import time if not event.isSet(): # 判断是否为真,如果不为真就设置为真,也就是一上来是绿灯 event.set() count = 0 # 初始化计数器,可以理解为红绿灯之间的等待时间 while True: # 无限制循环下去 if count < 10: # 0-10秒是绿灯 print('