并发编程 07.04
1.生产者消费者模型
解决方案:
- 先将双方解开耦合,让不同的进程负责不同的任务
 - 提供一个共享的容器,来平衡双方的能力,之所以使用进程队列就是因为队列可以在进程中共享
 
from multiprocessing import Process, Queue
import requests
import re,os,time,random
def product(urls,q): #生产者任务
    i = 0
    for url in urls:
        response = requests.get(url)
        text = response.text
        time.sleep(random.random())
        q.put(text)
        i += 1
        print(os.getpid(),'生产了第%s个数据'% i)
def customer(q): #消费者任务
    i = 0
    while True:
        text = q.get()
        time.sleep(random.random())
        res = re.findall('src=//(.*?)width',text)
        i += 1
        print('第%s个任务获取到%s个img'%(i,len(res)))
if __name__ == '__main__':
    urls = [
    "http://www.baidu.com",
    "http://www.taobao.com",
    "http://www.jd.com",
    "http://www.bilibili.com",
    ]
    q = Queue() #创建一个共享的容器,队列
    p1 = Process(target=product,args=(urls,q))
    p1.start() #开启生产者进程
    c = Process(target=customer,args=(q,))
    c.start() #开启消费者进程
2.joinableQuene
joinableQuene继承自Quene用法一致,增加了join和taskDone
join是个阻塞函数,会阻塞到taskdone的调用次数等于存入的元素个数,可以用于表示队列任务处理完成
from multiprocessing import  Process, JoinableQueue
import re,os,time,random
def product(q,name):
    for i in range(5):
        hot_dog = '%s的热狗%s' %(name,(i+1))
        time.sleep(random.random())
        print('生产了',hot_dog)
        q.put(hot_dog)
def customer(q):
    while True:
        hot_dog = q.get()
        time.sleep(random.random())
        print('消费了%s'% hot_dog)
        q.task_done()  #标记这个任务处理完成
if __name__ == '__main__':
    q = JoinableQueue() #创建一个双方能共享的容器
    
    p1 = Process(target=product,args=(q,'上海'))
    p2 = Process(target=product,args=(q,'北京'))
    p1.start()#开启生产者进程
    p2.start()
    c = Process(target=customer,args=(q,))
    c.daemon = True # 可以将消费者设置为守护进程 当主进程确认 任务全部完成时 可以随着主进程一起结束
    c.start()
    p1.join()
    p2.join()  #代码走到这里意味着生产完成
    q.join() #意味着队列中的任务都处理完成了
    c.terminate()# 直接终止消费者进程
- redis 消息队列
 - MQ 消息队列
 
3.多线程
线程是操作系统可以运算调度的最小单位,是真正的 执行单位,其包含在进程中,一个线程就是一条固定的控制流程
一个进程可以包含多个线程,同一进程中的线程共享进程内的资源
特点:系统会为每一个进程自动创建一条线程,称之为主线程,后续通过代码开启的线程称之为子线程
进程对比线程
进程是一个资源单位,首先是执行单位;创建进程的开销远大于线程
多个进程之间内存是相互隔离的,而线程是共享进程内的所有资源
进程之间对于硬件资源是竞争关系,而线程是协作关系;开启线程也是需要消耗资源的;进程之间有层级关系,而线程之间没有是平等的
为什么使用线程:
- 有多个任务要并发处理
 - 当要并发处理的任务有很多的时候,不能使用进程,进程资源开销太大,线程开销非常小,适用于任务数非常多的情况
 
4.线程的使用方法
方式1:
from threading import Thread
def task():
    print('子线程 run')
t = Thread(target=task) # 与进程不同之处1   不需要加判断 开启线程的代码放哪里都可以
t.start()
print('over')
class MyThread(Thread): #继承Thread类
    def run(self) -> None:
        print('子线程 run')
        
mt =MyThread()
mt.start()
print('over')
线程安全问题
只要访问了同一资源一定会产生安全问题,解决方案和多进程一致,就是给操作公共资源代码加锁
from threading import Thread,Lock
import time,random
a = 10
l = Lock()
def task():
    global a
    l.acquire()
    temp = a
    time.sleep(random.random())
    a = temp - 1
    l.release()
ts = []
for i in range(10):
    t = Thread(target=task)
    t.start()
    ts.append(t)
for t in ts:t.join()
print(a)
守护线程
一个线程a设置为b的守护线程,a会随着b的结束而结束
默认情况下 主线程即使代码执行完毕 也会等待所有非守护线程完毕后程序才能结束 因为多个线程之间是协作关系
from threading import Thread
import time
def task():
    print('妃子 start')
    time.sleep(5)
    print('妃子 over')
def task2():
    print('太后 start')
    time.sleep(3)
    print('太后 start')
print('皇帝 主线程 start')
t = Thread(target=task)
t.daemon = True
t.start()
t2 = Thread(target=task2)
t2.start()
print('皇帝 主线程 over')
线程中常用属性和方法
from threading import  Thread,current_thread,enumerate,activeCount
import time
t = Thread()
t.start()
t.join()
t.is_alive()
t.isAlive()
t.ident #线程标识符 id
t.daemon
print(current_thread()) # 获取当前线程对象
 t = Thread(target=lambda:print(current_thread()))
 t.start()
 t = Thread(target=lambda:time.sleep(1))
 t.start()
print(enumerate()) # 获取正在运行的所有线程对象  是一个列表
print(activeCount()) #存活的线程数量
                    
                
                
            
        
浙公网安备 33010602011771号