Python并发编程04 /多线程、生产消费者模型、线程进程对比、线程的方法、线程join、守护线程、线程互斥锁
Python并发编程04 /多线程、生产消费者模型、线程进程对比、线程的方法、线程join、守护线程、线程互斥锁
目录
1. 生产消费者模型
-
定义:编程思想,模型,设计模式,理论等等,都是一种编程的方法,遇到类似的情况,套用即可.
-
生产者消费者模型三要素:
生产者: 产生数据的
消费者: 接收数据做进一步处理的
容器: 队列,起到缓冲的作用,平衡生产力与消费力,解耦.
-
代码示例:
from multiprocessing import Process from multiprocessing import Queue import time import random def producer(q,name): for i in range(1,6): time.sleep(random.randint(1,2)) res = f'{i}号包子' q.put(res) print(f'生产者{name} 生产了{res}') def consumer(q,name): while 1: try: food = q.get(timeout=3) time.sleep(random.randint(1, 3)) print(f'\033[31;0m消费者{name} 吃了{food}\033[0m') except Exception: return if __name__ == '__main__': q = Queue() p1 = Process(target=producer,args=(q,'张三')) p2 = Process(target=consumer,args=(q,'李四')) p1.start() p2.start()
2. 线程的理论知识
-
什么是线程
标准描述开启一个进程:开启一个进程:进程会在内存中开辟一个进程空间,将主进程的资料数据全部复制一份,线程会执行里面的代码.
***进程是资源单位, 线程是执行单位;是操作系统调度的最小单元,是进程中的实际运作单位.
-
线程vs进程
- 开启进程的开销非常大,比开启线程的开销大很多.
- 开启线程的速度非常快.要快几十倍到上百倍.
- 同一进程内线程与线程之间可以共享数据,进程与进程之间需借助队列等方法实现通信.
-
线程的应用
单个进程开启三个线程.并发的执行任务.
并发:一个cpu 看起来像是同时执行多个任务.
-
主线程子线程没有地位之分
一个主线程在干活,当干完活了,得等待其他线程干完活之后,才能结束本进程.
3. 开启线程的两种方式
-
方式一
from threading import Thread import time def task(name): print(f'{name} is running') time.sleep(1) print(f'{name} is gone') if __name__ == '__main__': t1 = Thread(target=task,args=('张三',)) # args的参数一定要是元组 t1.start() print('===主线程') # 线程是没有主次之分的.
-
方式二
from threading import Thread import time class MyThread(Thread): def __init__(self,name,l1,s1): super().__init__() self.name = name self.l1 = l1 self.s1 = s1 def run(self): print(f'{self.name} is running') time.sleep(1) print(f'{self.name} is gone') if __name__ == '__main__': t1 = MyThread('张三', [1,2,3], '180') t1.start() print('=====主线程')
4. 线程、进程对比代码验证
-
开启速度对比
# 多进程 from threading import Thread from multiprocessing import Process import os def work(): print('hello') if __name__ == '__main__': t=Process(target=work) t.start() print('主线程/主进程') # 多线程 from threading import Thread import time def task(name): print(f'{name} is running') time.sleep(1) print(f'{name} is gone') if __name__ == '__main__': t1 = Thread(target=task,args=('张三',)) t1.start() print('===主线程') # 结论: # 多进程是先打印的'主线程/主进程',多线程是先打印的'张三 is running',所以得出线程的开启速度比进程快
-
对比pid
# 多进程 from multiprocessing import Process import time import os def task(name): print(f'子进程: {os.getpid()}') print(f'主进程: {os.getppid()}') if __name__ == '__main__': p1 = Process(target=task,args=('张三',)) # 创建一个进程对象 p2 = Process(target=task,args=('李四',)) # 创建一个进程对象 p1.start() p2.start() print(f'==主{os.getpid()}') # 多线程 from threading import Thread import os def task(): print(os.getpid()) if __name__ == '__main__': t1 = Thread(target=task) t2 = Thread(target=task) t1.start() t2.start() print(f'===主线程{os.getpid()}') # 结论: # 线程没有pid,进程才有pid
-
共享数据的对比,同一个进程内线程共享内部数据
from threading import Thread import os x = 3 def task(): global x x = 100 if __name__ == '__main__': t1 = Thread(target=task) t1.start() t1.join() print(f'===主线程{x}') # 同一进程内的资源数据对于这个进程内的多个线程来说是共享的.
5. 线程的其他方法
-
代码示例:
from threading import Thread from threading import currentThread from threading import enumerate from threading import activeCount import os import time def task(): print(currentThread()) # 获取当前线程对象 time.sleep(1) print('666') if __name__ == '__main__': t1 = Thread(target=task,name='线程1') # name 设置线程名 t2 = Thread(target=task,name='线程2') t1.start() t2.start() time.sleep(2) print(t1.isAlive()) # 判断线程是否活着 print(t1.getName()) # 获取线程名 t1.setName('子线程-1') # 设置线程的名称 print(t1.name) # 获取线程名 *** # threading方法 print(currentThread()) # 获取当前线程的对象 print(enumerate()) # 返回一个列表,包含所有的线程对象 print(activeCount()) # 获取当前线程存活个数 *** print(f'===主线程{os.getpid()}')
6. 线程join
-
join: 阻塞 告知主线程要等待我子线程执行完毕之后再执行主线程
-
代码示例:
from threading import Thread import time def task(name): print(f'{name} is running') time.sleep(1) print(f'{name} is gone') if __name__ == '__main__': start_time = time.time() t1 = Thread(target=task,args=('张三',)) t2 = Thread(target=task,args=('李四',)) t3 = Thread(target=task,args=('王五',)) t1.start() t1.join() t2.start() t2.join() t3.start() t3.join() print(f'===主线程{time.time() - start_time}') # 线程是没有主次之分的.
7. 守护线程
-
守护线程:等待非守护子线程以及主线程结束之后,结束.
-
代码示例:
示例一:简单使用守护线程
from threading import Thread import time def sayhi(name): print('你好!') time.sleep(2) print('%s say hello' %name) if __name__ == '__main__': t = Thread(target=sayhi,args=('张三',)) # 方式1: t.setDaemon(True) # 必须在t.start()之前设置 # 方式2: t.daemon = True t.start() print('主线程')
示例二:守护线程在非守护线程结束前先结束
from threading import Thread import time def foo(): print(123) # 第1步 time.sleep(1) print("end123") # 第4步 def bar(): print(456) # 第2步 time.sleep(3) print("end456") # 第5步 t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True t1.start() t2.start() print("---main---") # 第3步 # 结果: # 123 # 456 # ---main--- # end123 # end456
示例三:守护线程在非守护线程结束之后结束
from threading import Thread import time def foo(): print(123) # 第1步 time.sleep(3) print("end123") def bar(): print(456) # 第2步 time.sleep(1) print("end456") # 第4步 t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True t1.start() t2.start() print("main-------") # 第3步 # 结果: # 123 # 456 # main------- # end456
8. 线程互斥锁
-
多个任务公抢一个数据,保证数据的安全的目的,要让其串行
-
代码示例
from threading import Thread from threading import Lock import time import random x = 100 def task(lock): lock.acquire() global x temp = x time.sleep(0.01) temp = temp - 1 x = temp lock.release() if __name__ == '__main__': mutex = Lock() l1 = [] for i in range(100): t = Thread(target=task,args=(mutex,)) l1.append(t) t.start() for i in l1: i.join() print(f'主线程{x}')