一、线程(Thread)
1、定义:线程是操作系统能进行运算调度的最小单位,它包含在进程中,是进程的实际运作单位,一条线程是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。简单理解:线程是一系列指令的集合,操作系统通过这些指令调用硬件。
2、同一个线程中的所有线程共享同一个内存空间资源,
3、Python的多线程是伪多线程,是利用CPU上下文切换的方式造成并发效果,其实同时运行的只有一个线程。如下面的图1。
4、多线程代码:
import threading import time class MyThread(threading.Thread): """ # 用自定义一个子类的方式来启动线程 """ def __init__(self, n): super(MyThread, self).__init__() self.n = n def run(self): print("你好,%s" % self.n) time.sleep(2) start_time = time.time() thread_list = [] # 启动50个线程 for i in range(50): t1 = MyThread("t%s" % i) t1.start() thread_list.append(t1) # 等待所有线程执行完毕后主线程再继续执行 for i in thread_list: i.join() print("总共执行时间:%s" % float(time.time() - start_time))
5、全局解释器锁(GIL)
5.1、定义:GIL 是最流行的 CPython 解释器(平常称为 Python)中的一个技术术语,中文译为全局解释器锁,其本质上类似操作系统的 Mutex(即互斥锁,意思是我修改的时候你不能修改,也就是锁的意思)
5.2、功能:在 CPython 解释器中执行的每一个 Python 线程,都会先锁住自己,以阻止别的线程执行,这样在同个时间一个CPU只执行一个线程。当然,CPython 不可能容忍一个线程一直独占解释器,check interval 机制会在一个时间段后释放前面一个线程的全局锁执行下一个线程,以达到轮流执行线程的目的。这样一来,用户看到的就是“伪”并行,即 Python 线程在交替执行,来模拟真正并行的线程。
5.3、CPython 引进 GIL,可以最大程度上规避类似内存管理这样复杂的竞争风险问题,有了 GIL,并不意味着无需去考虑线程安全,因为即便 GIL 仅允许一个 Python 线程执行,但别忘了 Python 还有 check interval 这样的抢占机制。所以就要引入线程锁的机制,保证同个时间只有一个线程修改数据。
《图1》
5.4:线程锁的代码如下
import threading import time num = 0 lock_obj = threading.Lock() def run(): # 申请锁,使别的线程进不来 lock_obj.acquire() global num time.sleep(1.1) num = num + 1 # 解锁,解锁后别的线程可以进来 lock_obj.release() t_list = [] start_time = time.time() # 启动1000个线程 for i in range(100): t1 = threading.Thread(target=run) t1.start() t_list.append(t1) for i in t_list: i.join() time.sleep(3) print("num:%d" % num) print("time:%f" % float(time.time() - start_time))
6、递归锁:一个锁套另外一个锁,形成锁止循环,这种情况就要用到递归锁RLOCK
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) num, num2 = 0, 0 # 这里如果用Lock()就会无限循环,找不到具体用哪个钥匙打开锁,如果用RLock就不会,如果又多重锁嵌套的情况一定要用递归锁 lock = threading.Lock() for i in range(1): t = threading.Thread(target=run3) t.start() while threading.active_count() != 1: print("当前活跃的线程数:",threading.active_count()) else: print('----all threads done---') print("打印num和num2:",num, num2)
7、信号量(Semaphore):允许同时间最多几个线程进入执行,每出来一个进去一个,同时保持预先设置的线程最大允许数量。
import threading, time def run(n): semaphore.acquire() time.sleep(1) print("run the thread: %s " % n) semaphore.release() if __name__ == '__main__': semaphore = threading.BoundedSemaphore(5) # 最多允许5个线程同时运行 for i in range(22): 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)
8、事件(Event)
8.1、定义:通过标识位和状态,来实现线程之间的交互。简单说,就是一个标志位,只有两种状态,一种是设置(Event.set()),一直是没有设置(Event.clear())。
8.2、Event类还有两个方法,wait()等待被设定,isset()判断是否被设定
8.3、以下代码实现一个简单事件,一个线程控制红绿灯,另外一个线程控制车子,当红绿灯是红色的时候,车子停止,绿的时候,车子行驶的效果
import time import threading event = threading.Event() def lighter(): count = 0 event.set() # 刚开始的标识位先设置绿灯 while True: if 5 < count < 10: # 改成红灯 event.clear() # 把标志位清了 print("