共享内存(IPC进程间通信)
1.管道
一个进程写,一个进程读的情况,两个进程涉及不到数据安全的问题可以用管道来实现共享内存
2.队列(管道+锁)
队列使用于本地,不能基于网络通信
# 队列代码实现
from multiprocessing import Queue
q = Queue(3) # 列表里面存的值跟取的值都是放入q里面的,Queue(3)表示队列里面最多只能放3个值,如果超过3个值,那么该队列则处于堵塞状态
队列里面放值
q.put([1, 2, 3])
q.put({"a": 1})
q.put("xxxx")
q.put(100000)
q.put(1000, block=True, timeout=3) # block=True,可以通过timeout来指定超时时间,队列满了,超时会直接抛出异常
q.put(1000, block=False) # block=False,队列满了就直接抛出异常
队列里面取值
print(q.get())
print(q.get())
print(q.get())
print(q.get(block=False)) # block=False,取不到值就直接抛出异常
print(q.get(block=True, timeout=3)) # block=True,可以通过timeout来指定超时时间,取不到值的情况下,超时会直接抛出异常
生产者消费者模型
什么是生产者消费者模型:
该模型有两种角色:一种是生产者,另外一种是消费者
生产者负责产生数据,消费者负责取走数据进行处理
生产者与消费者通过队列通信
优点:解耦合,平衡了生成者的生产力与消费者的处理能力
生产者与消费者模型案例
方式1()
from multiprocessing import Process, Queue
import time
import random
def producer(q, name, food):
for i in range(3):
res = "%s %s" % (food, i)
time.sleep(random.randint(1, 3))
q.put(res)
print("%s 生成了 %s" % (name, res))
def consumer(q, name):
while True:
res = q.get()
if res is None:
break
time.sleep(random.randint(1, 3))
print("%s 吃了 %s" % (name, res))
if __name__ == '__main__':
q = Queue()
p1 = Process(target=producer, args=(q, "厨师1", "包子"))
p2 = Process(target=producer, args=(q, "厨师2", "烧卖"))
p3 = Process(target=producer, args=(q, "厨师3", "面条"))
c1 = Process(target=consumer, args=(q, "吃货1"))
c2 = Process(target=consumer, args=(q, "吃货2"))
p1.start()
p2.start()
p3.start()
c1.start()
c2.start()
p1.join()
p2.join()
p3.join()
q.put(None)
q.put(None)
print("主进程")
逻辑:先造产品,造好产品后,再运行主进程,主进程再增加两个None,按照队列依次取值的特性,最后两次取值只能取到None。
方式2
from multiprocessing import Process, JoinableQueue
import time
import random
def producer(q, name, food):
for i in range(3):
res = "%s %s" % (food, i)
time.sleep(random.randint(1, 3))
q.put(res)
print("%s 生成了 %s" % (name, res))
q.join() # 等队列的值都被取干净了,再结束
def consumer(q, name):
while True:
res = q.get()
time.sleep(random.randint(1, 3))
print("%s 吃了 %s" % (name, res))
q.task_done() # 每次执行一次取值都会发送一次信号
if __name__ == '__main__':
q = JoinableQueue()
p1 = Process(target=producer, args=(q, "厨师1", "包子"))
p2 = Process(target=producer, args=(q, "厨师2", "烧卖"))
p3 = Process(target=producer, args=(q, "厨师3", "面条"))
c1 = Process(target=consumer, args=(q, "吃货1"))
c2 = Process(target=consumer, args=(q, "吃货2"))
c1.daemon = True
c2.daemon = True
p1.start()
p2.start()
p3.start()
c1.start()
c2.start()
p1.join()
p2.join()
p3.join()
print("主进程")
线程理论
线程:一条流水线的运行过程(进程里面代码运行的过程就叫线程,与资源无关)
线程是一个执行单位,cup执行的就是线程
进程是一个资源单位
同一进程下的多个线程,共享进程资源;不同进程下的线程是不可以共享资源的
线程与进程的关系与差别
1.同一个进程下的多个线程共享该进程的内存资源
2.开启子线程的开销要远远小于开启子进程
用户空间线程与内核空间线程
用户空间线程:用户级线程内核的切换由用户态程序自己控制内核切换,
不需要内核干涉,少了进出内核态的消耗,但不能很好的利用多核cpu。
内核空间线程:切换由内核控制,当线程进行切换的时候,由用户态
转化为内核态。切换完毕要从内核态返回用户态。
python属于内核空间线程,由cup控制
开启线程的两种方式
开启进程的方式1
进程的特性1:开启子线程的开销要远远小于开启子进程
from threading import Thread, current_thread # current_thread
def task():
print("%s is running" % current_thread().name) # 先打印这一行,pid是一样的
# current_thread().name 会获得当前线程的名字
if __name__ == '__main__':
t = Thread(target=task)
t.start()
print("主线程", current_thread().name) # 再打印这一行,pid是一样的
# current_thread().name 会获得当前线程的名字
进程的特性2:同一个进程下的多个线程共享该进程的内存资源
from threading import Thread
n = 100
def task():
global n
n = 0
if __name__ == '__main__':
t = Thread(target=task)
t.start()
t.join()
print("主线程", n)
开启进程的方式2
from threading import Thread
class Mythread(Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self) -> None:
print("%s is running" % self.name)
if __name__ == '__main__':
t = Mythread("线程1")
t.start()
线程相关对象与方法
from threading import Thread, current_thread, active_count,enumerate
import time
def task():
print("%s is running" % current_thread().name)
time.sleep(5)
if __name__ == '__main__':
t = Thread(target=task)
t.start()
print(t.is_alive()) # is_alive()判断线程是否存活
print(active_count()) # 查看活着的线程数
print(enumerate()) # 显示了一个列表,里面是线程对象
守护线程(了解)
守护进程守护的是主进程的运行时间,守护线程守护的是主进程的生命周期
线程跟进程一样,主线程也是需要等子线程运行结束后,主线程才会结束
守护进程跟守护线程一样,都不能开启子进程或者子线程
from threading import Thread, current_thread, active_count
import os
import time
def task(n):
print("%s is run" % current_thread().name)
time.sleep(n)
print("%s is end" % current_thread().name)
if __name__ == '__main__':
t1 = Thread(target=task, args=(3,))
t2 = Thread(target=task, args=(5,))
t3 = Thread(target=task, args=(100,))
t3.daemon = True
t1.start()
t2.start()
t3.start()
print("主")
互斥锁
降低数据运行的效率,保护了数据安全。
from threading import Thread, Lock
import time
n = 100
mutex = Lock()
def task():
global n
with mutex:
teep = n
time.sleep(0.1)
n = teep - 1
if __name__ == '__main__':
thread_l = []
start_time = time.time()
for i in range(100):
t = Thread(target=task)
thread_l.append(t) # 把每一次运行的线程对象添加到列表里面(并发)
t.start()
for obj in thread_l:
obj.join() # 把线程对象依次加上join,这样一个线程抢到锁运行完后,下一次其他线程才能抢锁运行
print("主", n, time.time() - start_time)