全局解释器锁GIL:
python代码的执行由python虚拟机来控制,python在设计之初就考虑到要在主循环中,同时只有只有一个线程来执行。
在多线环境中,python虚拟机按以下方式执行:
1,设置GIL;
2,切换到一个线程去运行;
3,运行指定数量的字节代码指令或者线程主动让出控制(timg.sleep);
4,把线程设置成睡眠状态;
5,解锁GIL;
6,重复以上步骤
Python提供了几个用于多线程编程的模块,包括thread、threading和Queue等。thread和threading模块允许程序员创建和管理线程。thread模块提供了基本的线程和锁的支持,threading提供了更高级别、功能更强的线程管理的功能。Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
Threading用于提供线程相关的操作,线程是CPU调度的最小单位。
import threading import time def show(arg): time.sleep(1) print('thread'+str(arg)) for i in range(10): t = threading.Thread(target=show, args=(i,)) t.start() print('main thread stop')
上述代码创建了10个线程,然后等待cpu调度,分片执行指令。
import threading class MyThread(threading.Thread): def __init__(self,n): threading.Thread.__init__(self) #或者 super().__init__() self.n=n def run(self): print(self.n) t1=MyThread(1) t1.start() print('主线程1') t2=MyThread(2) t2.start() print('主线程2')
更多方法:
start 线程准备就绪,等待CPU调度
setName 为线程设置名称
getName 获取线程名称
run 线程被cpu调度后,自动执行线程对象的run方法
join(): 主线程A中,创建了子线程B,并且在主线程A中调用了B.join(),那么,主线程A会在调用的地方等待,直到子线程B完成操作后,
才可以接着往下执行,那么在调用这个线程时可以使用被调用线程的join方法。
setDeamon(True) 主线程A中,创建了子线程B,并且在主线程A中调用了B.setDaemon(),这个的意思是,把主线程A设置为守护线程,这时候,要是主线程A执行结束 了,就 不管子线程B是否完成,一并和主线程A退出.这就是setDaemon方法的含义,这基本和join是相反的。此外,还有个要特别注意的:必须在start() 方法调用之前设置,如果不设置为守护线程,程序会被无限挂起。
threading模块提供的一些方法:
isAlive(): 返回线程是否活动的。 threading.currentThread(): 返回当前的线程变量。 threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
import time def work(): time.sleep(1) print(threading.current_thread().getName()) if __name__ == '__main__': #在主进程下开启线程 t=threading.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 4048)> [<_MainThread(MainThread, started 4048)>, <Thread(Thread-1, started 2664)>] 2 主线程/主进程 Thread-1
from threading import Thread import time def task(n): time.sleep(1) print(n) for i in range(3): t=Thread(target=task,args=(i,)) t.start() t.join() print('主线程') print(t.is_alive()) >>> 0 主线程 False 1 主线程 False 2 主线程 False
锁
线程锁(Lock/Rlock)
由于线程之间是随机调度,并且每个线程执行指定字节或者时间,之后其它线程修改同一条数据时可能出现脏数据,所以出现了线程锁。
同步锁:
import threading import time n=0 lock=threading.Lock() def task(): global n lock.acquire() n += 1 time.sleep(1) print(n) lock.release() for i in range(10): t=threading.Thread(target=task) t.start() 输出结果:1-10 如果不加锁,当程序停1秒的时候,n+1已经运行了十遍,即n=10.
递归锁:
import threading import time n=0 lock=threading.RLock() def task(): global n lock.acquire() n += 1 lock.acquire() n+=1 time.sleep(1) print(n) lock.release() lock.release() for i in range(10): t=threading.Thread(target=task) t.start() 输出结果:2,4,6,8,10,12,14,16,18,20
信号量:
-- 同时允许一定数量的线程更改数据.
import threading, time def run(n): semaphore.acquire() time.sleep(1) print(n) semaphore.release() semaphore= threading.BoundedSemaphore(5) # 最多允许5个线程同时运行 for i in range(10): t = threading.Thread(target=run, args=(i,)) t.start()
事件:
-- python线程的事件用于主线程控制其他线程的执行
event.isSet():返回event的状态值;
event.wait():如果 event.isSet()==False将阻塞线程;
event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
event.clear():恢复event的状态值为False。
import threading def do(event): print('start') event.wait() #加锁:红灯(False) print('execute') event = threading.Event() for i in range(5): t=threading.Thread(target=do,args=(event,)) t.start() input('>>>') event.set() #绿灯(True) event.clear() #再次变红灯(False) for i in range(5): t=threading.Thread(target=do,args=(event,)) t.start() event.set()
条件:
使得线程等待,只有满足条件时,才释放n个线程.
import threading lock=threading.Condition() def task(n): lock.acquire() lock.wait() print(n) lock.release() for i in range(5): t=threading.Thread(target=task,args=(i,)) t.start() while True: inp=input('>>>') #输入几个,就释放几个 if inp == 'q': #输入q,就退出,什么也不执行 break lock.acquire() lock.notify(int(inp)) lock.release()
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()
线程池:
concurrent.futures模块提供了高度封装的异步调用接口
ThreadPoolExecutor:线程池,提供异步调用
from concurrent.futures import ThreadPoolExecutor def task(arg): print(arg) pool = ThreadPoolExecutor(5) for i in range(10): pool.submit(task,i) print('end')
线程队列:
-- queue队列
先进先出
import queue q=queue.Queue() q.put('first') q.put('second') q.put('third') print(q.get()) print(q.get()) print(q.get()) 输出结果: first second third
后进先出
import queue q=queue.LifoQueue() q.put('first') q.put('second') q.put('third') print(q.get()) print(q.get()) print(q.get()) 输出结果: third second first
优先级队列
import queue q=queue.PriorityQueue() #put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高 q.put((20,'a')) q.put((10,'b')) q.put((30,'c')) print(q.get()) print(q.get()) print(q.get()) 输出结果(数字越小优先级越高,优先级高的优先出队): (10, 'b') (20, 'a') (30, 'c')
threading.local()
类的对象.xx='xx' 调用对象的 setattr 方法 (类的对象.[xx]='xx' 调用对象的 __setitem__ 方法)
import time import threading INFO = {} class Local(object): def __getattr__(self, item): ident = threading.get_ident() return INFO[ident][item] def __setattr__(self, key, value): ident = threading.get_ident() if ident in INFO: INFO[ident][key] = value else: INFO[ident] = {key:value} obj = Local() def func(arg): obj.phone = arg # 调用对象的 __setattr__方法(“phone”,1) time.sleep(1) print(obj.phone,arg) for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start()
练习题:
class Foo(object): def __init__(self): object.__setattr__(self, 'info', {}) # 在对象中设置值的本质 def __setattr__(self, key, value): self.info[key] = value def __getattr__(self, item): print(item) return self.info[item] obj = Foo() obj.name = 'salah' print(obj.name) >>> name salah
多线程环境下,每一个线程均可以使用所属进程的全局变量。如果一个线程对全局变量进行了修改,将会影响到其他所有的线程。只用全局变量并不能满足多线程环境的需求,很多时候线程还需要拥有自己的私有数据,这些数据对于其他线程来说不可见。因此线程中也可以使用局部变量,局部变量只有线程自身可以访问,同一个进程下的其他线程不可访问。
但是有时候使用局部变量不太方便,因此 python 还提供了 ThreadLocal 变量,它本身是一个全局变量,但是每个线程却可以利用它来保存属于自己的私有数据,这些私有数据对其他线程也是不可见的。
import threading X = 'abc' ctx = threading.local() ctx.x = 123 # 主线程中定义x本地属性 print(ctx, type(ctx), ctx.x) def work(): print(X) print(ctx) print(ctx.x) # 子线程访问不到 threading.Thread(target=work).start() >>> <_thread._local object at 0x00000276D583C780> <class '_thread._local'> 123 abc <_thread._local object at 0x00000276D583C780>