并发编程3
目录
内置队列
from multiprocessing import Queue
q = Queue(5) # 自定义队列的长度
# put方法向队列中存放数据
q.put(111)
q.put(222)
q.put(333)
q.put(444)
print(q.full()) # False,full()用于判断队列是否已满
q.put(555)
print(q.full()) # True
# q.put(666) # 超出最大长度会原地等待队列出现空位
print(q.get())
print(q.get())
print(q.get())
print(q.get())
print(q.empty()) # False,empty()判断队列是否为空
# print(q.get())
print(q.empty()) # True
# print(q.get()) # 队列中没有值,会阻塞等待
print(q.get_nowait()) # 555,队列中如果没有值会报错
IPC机制
from multiprocessing import Process, Queue
def producer(q):
q.put('子进程producer往队列中添加值')
def consumer(q):
print('子进程consumer从队列中取值>>>:', q.get())
if __name__ == '__main__':
q = Queue()
p = Process(target=producer, args=(q,))
p1 = Process(target=consumer, args=(q,))
p.start()
p1.start()
q.put(999) # 主进程往队列中存放数据999
# print(q.get())
print('主进程')
生产者消费者模型
from multiprocessing import Process, Queue
import time
import random
def producer(name, food, q):
for i in range(1, 6):
data = f'{name}上架了{food}{i}'
print(data)
time.sleep(random.randint(1, 3)) # 模拟产生过程
q.put(data)
def consumer(name, q):
while True:
food = q.get()
time.sleep(random.random())
print(f'{name}购买了{food}')
if __name__ == '__main__':
q = Queue()
p1 = Process(target=producer, args=('tmall', '康师傅老坛酸菜面', q))
p2 = Process(target=producer, args=('jd', '统一老坛酸菜面', q))
c1 = Process(target=consumer, args=('杰杰', q))
c2 = Process(target=consumer, args=('多多', q))
c1.daemon = True
c2.daemon = True
p1.start()
p2.start()
c1.start()
c2.start()
p1.join()
p2.join()
q.put(None) # 结束信号的个数要跟消费者个数一致
q.put(None)
print('主线程')
from multiprocessing import Process, JoinableQueue
import time
import random
def producer(name, food, q):
for i in range(1, 6):
data = f'{name}上架了{food}{i}'
print(data)
time.sleep(random.randint(1, 3)) # 模拟产生过程
q.put(data)
def consumer(name, q):
while True:
food = q.get()
time.sleep(random.random())
print(f'{name}购买了{food}')
q.task_done() # 每次拿数据必须给队列一个反馈
if __name__ == '__main__':
q = JoinableQueue()
p1 = Process(target=producer, args=('tmall', '康师傅老坛酸菜面', q))
p2 = Process(target=producer, args=('jd', '统一老坛酸菜面', q))
c1 = Process(target=consumer, args=('杰杰', q))
c2 = Process(target=consumer, args=('多多', q))
c1.daemon = True
c2.daemon = True
p1.start()
p2.start()
c1.start()
c2.start()
p1.join()
p2.join()
q.join() # 等待队列中数据全部取出
print('主线程')
线程理论
什么是线程
进程:资源单位
线程:执行单位
进程相当于车间,线程相当于车间里面的流水线,一个进程中至少有一个线程
为什么要有线程
开设线程的消耗远远小于进程
开进程
- 申请内存空间
- 拷贝代码
开线程
一个进程内可以开设多个线程,无需申请内存空间和拷贝代码
一个进程内的多个线程数据是共享的
开设线程的两种方式
# 方式一
from threading import Thread
import time
def task(name):
print(f'{name} is running')
time.sleep(2)
print(f'{name} is over')
if __name__ == '__main__':
t = Thread(target=task, args=('mi_chat',))
t.start()
print('主线程')
# 方式二
from threading import Thread
import time
class MyThread(Thread):
def __init__(self, thread_name):
super().__init__()
self.thread_name = thread_name
def run(self):
print(f'{self.thread_name} jason is running')
time.sleep(2)
print(f'{self.thread_name} is over')
if __name__ == '__main__':
t = MyThread('Net_ease_music')
t.start()
print('主线程')
线程实现TCP服务端并发
# 客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8088))
while True:
send_msg = input('请输入发送的消息>>>:').strip()
client.send(send_msg.encode('utf8'))
data = client.recv(1024)
print(data.decode('utf8'))
# 服务端
import socket
from threading import Thread
server = socket.socket()
server.bind(('127.0.0.1', 8088))
server.listen()
def talk(sock):
while True:
data = sock.recv(1024)
print(data.decode('utf8'))
sock.send(data.upper())
while True:
sock, addr = server.accept()
t = Thread(target=talk, args=(sock,))
t.start()
线程join方法
from threading import Thread
import time
def task(thread_name):
print(f'{thread_name} is running')
time.sleep(2)
print(f'{thread_name} is over')
t = Thread(target=task, args=('youtube_shorts', ))
t.start()
t.join()
print('主线程')
线程间数据共享
from threading import Thread
nationality = 'China'
def task():
global nationality
nationality = 'UK'
t = Thread(target=task)
t.start()
t.join()
print(nationality)
线程对象属性和方法
验证一个进程下的多个线程是否真的处于一个进程
from threading import Thread, active_count, current_thread
import os
import time
def task():
time.sleep(1)
print('子线程获取进程号>>>:', os.getpid())
t = Thread(target=task)
t1 = Thread(target=task)
t.start()
t1.start()
print('主线程获取进程号>>>:', os.getpid())
统计进程下活跃的线程数
active_count()
from threading import Thread, active_count, current_thread
import os
import time
def task():
time.sleep(1)
print('子线程获取进程号>>>:', os.getpid())
t = Thread(target=task)
t1 = Thread(target=task)
t.start()
t1.start()
print(active_count())
获取线程的名字
from threading import Thread, current_thread
import os
import time
def task():
time.sleep(1)
print('子线程获取进程号>>>:', os.getpid())
print(current_thread().name)
t = Thread(target=task)
t1 = Thread(target=task)
t.start()
t1.start()
print(current_thread().name)
守护线程
主线程要等待所有非守护线程结束才可以结束
from threading import Thread
import time
def task(thread_name):
print(f'{thread_name} is running')
time.sleep(2)
print(f'{thread_name} is over')
if __name__ == '__main__':
t1 = Thread(target=task, args=('qq_zone',))
t2 = Thread(target=task, args=('qq_show',))
t1.daemon = True
t1.start()
t2.start()
print('主线程')
GIL全局解释器锁
GIL全局解释器锁只存在于CPython解释器,不是python的特征
GIL是一把用于阻止同一个进程下的多个线程同时执行的互斥锁
存在的原因是CPython解释器中的垃圾回收机制不是线程安全的
反向验证GIL的存在是必要的,如果不存在会产生垃圾回收机制与正常线程之间数据错乱
GIL是加在CPython解释器上的互斥锁,同一个进程下的多个线程要执行必须先抢GIL锁
GIL全局解释器锁的存在让同一个进程下多个线程肯定不能同时运行,无法发挥多核优势