python并发编程之多线程
一 什么是线程
在传统操作系统中,每一个进程都有一个地址空间,而且默认就有一个控制线程
线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程工作车间负责吧资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线
进程只是用来把资源集中到一起,二线程才是cpu上的的执行单位
线程是代码的运行过程
二 为何要用线程
1.同一个进程下,多个线程共享该进程下的资源
2.线程比进程开销要小得多
三 开启线程的两种方式
#方式一 from threading import Thread import time def sayhi(name): time.sleep(2) print('%s say hello' %name) if __name__ == '__main__': t=Thread(target=sayhi,args=('egon',)) t.start() print('主线程')
#方式二 from threading import Thread import time class Sayhi(Thread): def __init__(self,name): super().__init__() self.name=name def run(self): time.sleep(2) print('%s say hello' % self.name) if __name__ == '__main__': t = Sayhi('egon') t.start() print('主线程')
四 线程相关的其他方法
Thread实例对象的方法 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
from threading import Thread import threading from multiprocessing import Process import os def work(): import time time.sleep(3) print(threading.current_thread().getName()) if __name__ == '__main__': #在主进程下开启线程 t=Thread(target=work) t.start() print(threading.current_thread().getName()) print(threading.current_thread()) #主线程 print(threading.enumerate()) #连同主线程在内有两个运行的线程 print(threading.active_count()) print('主线程/主进程') ''' 打印结果: MainThread <_MainThread(MainThread, started 140735268892672)> [<_MainThread(MainThread, started 140735268892672)>, <Thread(Thread-1, started 123145307557888)>] 主线程/主进程 Thread-1 '''
五 守护线程
from threading import Thread import time def sayhi(name): time.sleep(2) print('%s say hello' %name) if __name__ == '__main__': t=Thread(target=sayhi,args=('egon',)) t.setDaemon(True) #必须在t.start()之前设置 t.start() print('主线程') print(t.is_alive()) ''' 主线程 True '''
六 死锁现象与递归锁
所谓死锁:是指两个或以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待现象,如无外力作用, 它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。
from threading import Thread,Lock import time mutexA=Lock() mutexB=Lock() class MyThread(Thread): def run(self): self.func1() self.func2() def func1(self): mutexA.acquire() print('\033[41m%s 拿到A锁\033[0m' %self.name) mutexB.acquire() print('\033[42m%s 拿到B锁\033[0m' %self.name) mutexB.release() mutexA.release() def func2(self): mutexB.acquire() print('\033[43m%s 拿到B锁\033[0m' %self.name) time.sleep(2) mutexA.acquire() print('\033[44m%s 拿到A锁\033[0m' %self.name) mutexA.release() mutexB.release() if __name__ == '__main__': for i in range(10): t=MyThread() t.start() ''' Thread-1 拿到A锁 Thread-1 拿到B锁 Thread-1 拿到B锁 Thread-2 拿到A锁 然后就卡住,死锁了 '''
解决方法,递归锁,在Python中为了支持在同一线程中的多此请求同一资源,Python提供了可重入锁RLock。
七信号量Semaphore
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release()时内置计数器+1;
计数器不能小于0;当计数器为 0时,acquire()将阻塞线程直到其他线程调用release()。
实例(同时只有5 个线程可以获得Semaphore,即可以限制最大连接数为5)
from threading import Thread, Semaphore # Semaphore 信号量 import time, random sm = Semaphore(5) def task(name): sm.acquire() print('%s 正在上厕所' % name) time.sleep(random.randint(1, 3)) sm.release() # 释放锁 if __name__ == '__main__': for i in range(20): t1 = Thread(target=task, args=('路人%s' % i,)) t1.start()
八 Event
同进程一样
线程的一个关键特性是每个线程都是独立运行且状态不可测。如果程序之中的其他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这个问题,我们需要使用threading库中的Event对象。对象包含一个可由线程设置的信号标志,允许线程等待某些事件的发生。在初始状态下Event中的信号标志被设置成假。一个线程如果将一个Event对象的信号标志设置为真,他将唤醒所有等待这个EEevent对象的线程。如果一个线程已经被设置为真的Event对象,那么他将忽略这个事件,继续执行
event.isSet():返回event的状态值; event.wait():如果 event.isSet()==False将阻塞线程; event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度; event.clear():恢复event的状态值为False。
# 一个线程运行完,立刻通知下一个线程开始运行 from threading import Thread, Event import time event = Event() def light(): print('红灯正亮着') time.sleep(3) event.set() #绿灯亮 def car(name): print('车辆%s 正在等绿灯' % name) event.wait() # 等灯变绿 print('车%s通行'%name) if __name__ == '__main__': # 红绿灯 t1 = Thread(target=light) t1.start() # 车 for i in range(10): t = Thread(target=car, args=(i,)) t.start()