线程
进程是资源的单位,而线程是执行单位。开启进程需要内存空间,这就好比造一个生产车间,好比进程内地代码就是生产资料,有变量,有函数,有类等等。而进程就是生产线,把这些生产资料放在线程这条生产线,才能生产出结果。每个进程都自带线程,线程才是真正的执行单位,进程为线程提供资源。
线程与进程(进程:https://www.cnblogs.com/huaiXin/p/11329260.html)。进行比较,开启进程需要申请内存空间,还需要‘拷贝’主进程的代码。但是开启线程不需要申请内存空间。一个进程内可以有多个线程,并且线程与线程之间是数据共享的。
开启现成的两种方式:
开启线程需要引入threading模块中的Thread这个类。开启方式一种是引用Thread类开启,另一种继承Thread类,自定义一个类开启。
''' 开启线程的第一种方式 ''' from threading import Thread # 调用线程模块 import time def task(name): print(f'{name}来了') time.sleep(3) print(f"{name}结束了") t = Thread(target=task, args=('旷哥',)) # 创建线程对象 t.start() # 启动线程 print('主')
''' 开启线程的第二种方式 ''' from threading import Thread import time class MyThread(Thread): # 继承Thread这个类 def __init__(self, name): super().__init__() self.name = name def run(self): # 写run方法 print(f'{self.name}来了') time.sleep(2) print(f'{self.name}结束') t = MyThread('旷哥') t.start() print('主')
线程对象及其他方法
''' current_thread : 查看线程对象方法 os.getpid: 查看PID端口号 active_count: 查看当前活跃的线程数 t.daemon: 设置伴随进程 ''' from threading import Thread, current_thread ,active_count # 调用线程模块 import time import os def task(name): print(f'{name}来了') print('子',os.getpid()) # 查看PID端口号 print('current_thread:', current_thread().name) time.sleep(3) print(f"{name}结束了") t = Thread(target=task, args=('旷哥',)) # 创建线程对象 t.daemon = True t.start() # 启动线程 print('主',os.getpid()) print(active_count()) # 查看当前活跃的线程数 print('主',current_thread().name)
线程互斥锁
当多个线程同时操作统一数据,数据是不安全的,需要加锁处理,讲线程并行操作数据改为串行操作数据。
''' 线程互斥锁 当多个线程同时操作统一数据,数据是不安全的 需要加锁,讲线程并行操作数据改为串行操作数据 ''' from threading import Thread, Lock import time n = 100 def func(L): global n L.acquire() # 抢锁 temp = n time.sleep(0.1) # 模拟网络延迟 temp -= 1 n = temp L.release() # 放锁 t_list = [] numtext = Lock() for i in range(100): # 开启100个线程 t = Thread(target=func, args=(numtext,)) t.start() t_list.append(t) for k in t_list: k.join() # 等待所有线程结束 print(n) # 打印共享的N
GIL全局解释器锁
GLI(global interproter Lock)是cpython全局解释器锁。它的本质是一把互斥锁,将并发变成串行牺牲效率保证安全性。cpython中每一个进程都有自身的线程,python中的垃圾回收机制,也是一行代码和模块,启动python是也需要启动垃圾回收机制代码,这也需要一个线程。它们都需要python解释器经行编译运行,而每个进程只有一个python解释器,进程下的所有线程都共用这个解释器,为了保证线程安全运行,就需要GIL对线程进行保护。GIL的特点:单进程下无法利用多核优势。而且多不同的数据加载不同的锁处理。
python开多线程多进程,视情况而定。当计算密集型时多任务,单核条件下应使用开多线程:多核条件下计算量在较小时开启多线程,如果不是就开启多进程。当I/O密集型多任务是无论单核多核,开启多线程。
''' 计算密集型 总结: 计算密集型在单核条件下。应开线程, 多核条件下, 在计算量百万次一下开线程,以上开进程 ''' import time from multiprocessing import Process from threading import Thread import os def sun(): i = 0 while i < 1e5: i += 1 if __name__ == '__main__': p_list = [] print(os.cpu_count()) start = time.time() for i in range(4): # p = Process(target=sun,) # 0.9663281440734863 p = Thread(target=sun,) # 0.37197113037109375 p.start() p_list.append(p) for p in p_list: p.join() print(f'用时{time.time()-start}') ''' I/O密集型 总结: 单核与多核条件开启线程 ''' import time from multiprocessing import Process from threading import Thread import os def sun(): i = 0 while i < 1e3: time.sleep(0.1) i += 1 if __name__ == '__main__': p_list = [] print(os.cpu_count()) start = time.time() for i in range(4): # p = Process(target=sun,) # 11.024189472198486 p = Thread(target=sun,) # 10.05493450164795 p.start() p_list.append(p) for p in p_list: p.join() print(f'用时{time.time()-start}')
死锁现象与递归锁
当使用锁过多时,可能会出现死锁现象。
''' 死锁现象 ''' import time from threading import Thread, Lock muxB = Lock() muxA = Lock() class MyThread(Thread): def run(self): self.func1() self.func2() pass def func1(self): muxA.acquire() print(f'{self.name}抢到A锁') muxB.acquire() print(f'{self.name}抢到B锁') muxB.release() print(f'{self.name}释放B锁') muxA.release() print(f'{self.name}释放A锁') def func2(self): muxB.acquire() print(f'{self.name}抢到B锁') time.sleep(0.1) muxA.acquire() print(f'{self.name}抢到A锁') muxA.release() print(f'{self.name}释放A锁') muxB.release() print(f'{self.name}释放B锁') for i in range(10): t = MyThread() t.start() # 结果 Thread-1抢到A锁 Thread-1抢到B锁 Thread-1释放B锁 Thread-1释放A锁 Thread-2抢到A锁 Thread-1抢到B锁 # 卡在此处
递归锁
递归锁是threading模块下RLock。这中锁多个对象每抢一次锁,内部加1计数,每释放一次就减一。计数标志为0时,其他对象才可以抢锁。且共用一把锁。
''' 递归锁 ''' import time from threading import Thread, RLock muxB = muxA = RLock() class MyThread(Thread): def run(self): self.func1() self.func2() pass def func1(self): muxA.acquire() print(f'{self.name}抢到A锁') muxB.acquire() print(f'{self.name}抢到B锁') muxB.release() print(f'{self.name}释放B锁') muxA.release() print(f'{self.name}释放A锁') def func2(self): muxB.acquire() print(f'{self.name}抢到B锁') time.sleep(0.1) muxA.acquire() print(f'{self.name}抢到A锁') muxA.release() print(f'{self.name}释放A锁') muxB.release() print(f'{self.name}释放B锁') for i in range(10): t = MyThread() t.start()
信号量
也是一种锁的应用。在threading模块下Semaphore方法。控制锁的个数。
import time from threading import Thread, Semaphore s = Semaphore(2) # 控制锁的各所,同时有两个锁 n = 10 def func1(i): global n s.acquire() temp = n time.sleep(1) temp -= 1 n = temp print(f'{i}操作后{n}') s.release() for i in range(10): t = Thread(target=func1, args=(i,)) t.start()
event事件
在threading模块下的Event方法,使子进程之间可会互相等待。
import time from threading import Thread, Event e = Event() def func1(): print('红灯亮起!') time.sleep(3) print('绿灯亮起!') e.set() def func2(name): print(f'{name}准备!') e.wait() print(f'{name}出发!') # 线程t1等待线程t t = Thread(target=func1, ) t.start() for i in range(10): t1 = Thread(target=func2, args=(f'车手{i}号',)) t1.start()