线程
生产者消费者模型
生产者消费者模型
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。
为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
# 基于消息队列的生产者消费者模型
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()
开启线程的两种方式
线程简介
进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。
进程是资源单位, 线程是执行单位.
描述开启一个进程的过程:
开启一个进程:进程会在内存中开辟一个进程空间,将主进程的资料数据全部复制一份,线程会执行里面的代码.
方式1:(函数型)
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__':
t = Thread(target=task,args=('海狗',))
t.start()
print('==主')
方式2:(类)
from threading import Thread
import time
class Mythread(Thread):
def __init__(self,name):
super().__init__()
self.name =name
def run(self):
print(f"{self.name} is running")
time.sleep(2)
print(f"{self.name} is gone")
if __name__ == '__main__':
t = Mythread('海狗')
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' # 线程的开启速度非常快
'===主线程'
'海狗 is gone'
线程的一些方法
from threading import Thread
from threading import currentThread
from threading import enumerate
from threading import activeCount
import time
import os
def task():
time.sleep(2)
print('666')
if __name__ == '__main__':
t1 = Thread(target=task,name='线程1')
t2 = Thread(target=task,name='线程2')
t1.start()
t2.start()
print(t1.isAlive()) # 判断线程是否还在活动 返回布尔值
print(t1.getName()) # 获取线程名称
t1.setName('gou') # 设置线程名称
print(t1.name)
print(currentThread()) # 获取当前线程
print(enumerate()) # 获取当前进程下所有线程,并以列表形式返回
print(activeCount()) # 获取还在活动的线程数
print(f"==主线程{os.getpid()}")
# 结果:
True
线程1
gou
<_MainThread(MainThread, started 11472)>
[<_MainThread(MainThread, started 11472)>, <Thread(gou, started 1876)>, <Thread(线程2, started 8760)>]
3
==主线程12980
666
666
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=('海狗1',))
t3 = Thread(target=task,args=('海狗2',))
t1.start()
t1.join()
t2.start()
t2.join()
t3.start()
t3.join()
print(f'===主线程{time.time() - start_time}') # 线程是没有主次之分的.
# 结果:
海狗 is running
海狗 is gone
海狗1 is running
海狗1 is gone
海狗2 is running
海狗2 is gone
===主线程3.0049641132354736
守护线程:
守护线程 等待非守护子线程以及主线程结束之后,结束.
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=('egon',))
# t.setDaemon(True) #必须在t.start()之前设置
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
面试题:
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
import random
x = 100
def task():
global x
temp = x
time.sleep(random.randint(1, 3))
temp = temp - 1
x = temp
if __name__ == '__main__':
l1 = []
for i in range(100):
t = Thread(target=task)
l1.append(t)
t.start()
for i in l1:
i.join()
print(f'主线程{x}')
# 结果: # 相当于对全局变量赋值100次99
主线程99
from threading import Thread
from threading import Lock
import time
import random
x = 100
def task(lock):
lock.acquire()
# time.sleep(random.randint(1,2))
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()
time.sleep(3)
print(f'主线程{x}')
# 结果:
主线程0