并发编程 07.04

1.生产者消费者模型

解决方案:

  1. 先将双方解开耦合,让不同的进程负责不同的任务
  2. 提供一个共享的容器,来平衡双方的能力,之所以使用进程队列就是因为队列可以在进程中共享
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.多线程

线程是操作系统可以运算调度的最小单位,是真正的 执行单位,其包含在进程中,一个线程就是一条固定的控制流程

一个进程可以包含多个线程,同一进程中的线程共享进程内的资源

特点:系统会为每一个进程自动创建一条线程,称之为主线程,后续通过代码开启的线程称之为子线程

进程对比线程

进程是一个资源单位,首先是执行单位;创建进程的开销远大于线程

多个进程之间内存是相互隔离的,而线程是共享进程内的所有资源

进程之间对于硬件资源是竞争关系,而线程是协作关系;开启线程也是需要消耗资源的;进程之间有层级关系,而线程之间没有是平等的

为什么使用线程:

  1. 有多个任务要并发处理
  2. 当要并发处理的任务有很多的时候,不能使用进程,进程资源开销太大,线程开销非常小,适用于任务数非常多的情况

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()) #存活的线程数量
posted @ 2019-07-04 19:25  海森t  阅读(40)  评论(0)    收藏  举报