线程、线程互斥锁、线程池
今日学习总结:
一、threading 是创建线程的模块
1.Thread 是一个类,用来创建线程的
2.current_thread 是一个方法,可以获取线程的名字:用 current_thread().name
3. Lock() 是用来产生互斥锁的
一、线程
1.什么是线程:进程是资源单位。线程是执行单位
线程与进程都是虚拟的概念,为了更好的表达某种事物。
注意:开启一个进程,一定会自带一个线程,线程才是真正的执行者
2.为什么要使用线程:为了节省资源的占用
01.开启进程:
会产生一个内存空间,申请一块资源。
会自带一个主线程
开启子进程的速度要比开启子线程的速度慢
02.开启线程
一个进程内可以开启多个线程,从进程的内存空间中申请执行单位
节省资源
03.开启三个进程
占用三份内存资源
04.开启三个线程
从一个内存资源中,申请三个小的执行单位
05.IO密集型:用多线程
因为IO时间可能由用户定
阻塞 ---> 切换 + 保存状态
06. 计算密集型:用多进程
因为计算时间由操作系统定
计算时间很长 ---> 切换 + 保存状态
3. 怎么使用线程
注意:进程与进程之间的数据是隔离的,线程与线程之间的数据是共享的
启动线程的两种方式:
方式一:用 Thread 类(线程与线程之间的数据是共享的)
from threading import Thread import time number = 1000 def task(): global number number = 100 print('start...') time.sleep(1) print('end...') if __name__ == '__main__': t = Thread(target=task) # 开启一个子线程 t.start() # t.join() print('主线程(主进程)...') print(number) 结果:100 ???? 因为同一进程内的线程之间共享进程内的数据
方式二:定义一个类继承Thread 类
from threading import Thread import time class MyThread(Thread): def run(self): print('start...') time.sleep(1) print('end...') if __name__ == '__main__': t = MyThread() #调用类,开启一个子线程 t.start() # t.join() print('主进程(主线程)...') 结果: start... 主进程(主线程)... end...
二、守护线程: .daemon = True 或者用 .setDaemon(True) #必须在t.start()之前设置
主线程结束,则子线程结束。
 
from threading import Thread from threading import current_thread import time number = 1000 def task(): global number number = 100 print(f'start...{current_thread().name}') time.sleep(3) print(f'end...{current_thread().name}') if __name__ == '__main__': # 开启一个子线程 for line in range(10): t = Thread(target=task) t.daemon = True # 加上守护线程: 主进程结束,代表主线程也结束,子线程有可能未被回收。 t.start() # t.join() print(f'主线程(主进程)...{current_thread().name}') print(number) 结果: start...Thread-1 start...Thread-2 start...Thread-3 start...Thread-4 start...Thread-5 start...Thread-6 start...Thread-7 start...Thread-8 start...Thread-9 start...Thread-10 主线程(主进程)...MainThread 100 Process finished with exit code 0
1 主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束,
2 主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护
三、线程互斥锁:让并发变成串行, 牺牲了执行效率, 保证了数据安全。
案例:开启10个线程,对一个数据进行修改
没有模拟网络延迟
from threading import Thread import time number = 100 def task(): global number number -=1 if __name__ == '__main__': for line in range(10): t = Thread(target=task) t.start() time.sleep(3) print(number) # 90
有模拟网路延迟
from threading import Thread
import time
number = 100
def task():
global number
number2=number
time.sleep(1)
number = number2-1
if __name__ == '__main__':
list1=[]
for line in range(10):
t = Thread(target=task)
t.start()
list1.append(t)
for t in list1:
t.join()
print(number) # 99
 
有模拟网路延迟 问题出现。结果是99 不是90
解决问题代码:给线程添加互斥锁
from threading import Thread import time from threading import Lock number = 100 lock=Lock() def task(): global number lock.acquire() number2=number time.sleep(1) number = number2-1 lock.release() if __name__ == '__main__': list1=[] for line in range(10): t = Thread(target=task) t.start() list1.append(t) for t in list1: t.join() print(number) # 90
四、线程池:一次性创建线程的个数。
作用:限制线程的数量,保证了硬件跟得上软件的发展。如100000个人访问你,就要开启100000个线程。硬件跟不上就会崩溃
ThreadPoolExecutor 是一个类,用来产生 线程池的
from concurrent.futures import ThreadPoolExecutor import time pool = ThreadPoolExecutor(100) # pool一次性只能创建100个线程 def task(line): print(line) time.sleep(10) if __name__ == '__main__': for line in range(1000): pool.submit(task, line) #submit(函数名,函数接受的参数) 结果: 每隔10秒,一次性产生100个数字
五、进程池:一次性创建进程的个数。
作用:限制线程的数量,保证了硬件跟得上软件的发展。
进程池 ProcessPoolExecutor 是一个类,用来产生 进程池的
六、自定义的回调函数
from concurrent.futures import ThreadPoolExecutor pool = ThreadPoolExecutor(50) # pool线程池对象 def task(): return '刺客伍六七' def callback(res_obj): result = res_obj.result() # 刺客伍六七 return result pool.submit(task).add_done_callback(callback) # add_done_callback 是一个函数,def add_done_callback(self, fn)

 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号