多线程之生产消费模型,多线程知识

进程:

生产者,消费者模型

在编程中,模型就是解决某个问题的固定方法或套路

生产者:泛指产生数据的一方

消费者:泛指处理数据的一方

生活案例:

​ 食堂饭店是生产者,负责炒菜

​ 吃饭的人就是消费者,负责吃

1. 要解决什么问题?

效率低,双方的处理速度不同,一方慢,一方快,则双方需要相互等待,比如:生产方一分钟能10个包子,它有十个盘子;但是消费者4分钟才能吃完一个馒头。生产方做完10个馒头,就会停止生产,因为盘子不够用,用时10分钟。消费者4分钟吃完一个馒头,也就是说生产方要到4分钟之后才能再生产一个馒头,依次类推。

在这个案例中,其实生产方的效率是很高的,但是由于消费者的效率低,导致整个生产消费链时间拉长,那么对于这样的情况,我们如何解决了?

第一:增加更多的盘子。
第二:增加更多的消费者。
第三:找一个大型的容器,对于生产方来说,它只需要把生产的馒头放到容器中即可;对于消费者来说,它只管从容器中拿东西即可。(这个大型的容器,就是下面引入的内容:队列)

2. 解决办法:在生产者和消费者之间加入队列

import time,random
import multiprocessing

def product(q,name):
    for i in range(5):
        dog = "%s的热狗%s"%(name,(i+1))
        time.sleep(random.random())
        print("生产了",dog)
        q.put(i+1)
    q.put(None)		# 生产者生产完毕后,在队列最后发送一个None,表示生产者生产完毕

def customer(q,name):
    while True:
        dog = q.get()
        if not dog:		# 判断生产何时结束
            break
        time.sleep(random.random())
        print("消费了%s的%s"%(name,dog))


if __name__ == '__main__':
    q = multiprocessing.Queue()

    # 生产者
    p1 = multiprocessing.Process(target=product,args=(q,"上海"))
    # p2 = multiprocessing.Process(target=product,args=(q,))
    p1.start()
    # p2.start()

    # 消费者
    c = multiprocessing.Process(target=customer,args=(q,"北京"))
    c.start()

    p1.join()
    c.join()
    print("结束")

问题:如果此时加入两个生产者,那么消费者如何判断两个生产者何时结束?

解决办法:joinableQueue

import multiprocessing
import time, random


# 生产者
def product(q, name):
    for i in range(5):
        dog = "%s的热狗%s" % (name, (i + 1))
        time.sleep(random.random())
        print("生产了", dog)
        q.put(dog)


# 消费者
def customer(q):
    while True:
        dog = q.get()
        time.sleep(random.random())
        print("消费了%s" % (dog))
        q.task_done()


# dliffy diagran

if __name__ == '__main__':
    q = multiprocessing.JoinableQueue()

    # 生产者
    p1 = multiprocessing.Process(target=product, args=(q, "上海"))
    p2 = multiprocessing.Process(target=product, args=(q, "北京"))
    p1.start()
    p2.start()

    # 消费者
    c = multiprocessing.Process(target=customer, args=(q,))
    c.start()

    # 结束生产进程
    p1.join()
    p2.join()

    # 结束队列进程
    q.join()                # 阻塞,等待队列进程任务执行完毕,再结束

    # 由以上两点,可以确定消费者进程一定被消费完了
    print("结束")

线程

1. 什么是线程

线程是操作系统可以运算调度的最小单位,是真正的执行单位,其包含在进程中。

一个线程就是一条固定的控制流程,一个进程可以包含多个线程

同一个进程中的线程共享进程内的资源

进程是操作系统可以调度或进程资源分配的基本单位,是一个资源单位,其中包含了运行这个程序所需的资源

2. 进程与线程的区别

进程是一个资源单位,线程是执行单位

创建进程的开销远大于线程

多个进程是隔离的,通信需要借助其他模块。而线程是共享进程内的所有资源

进程之间是竞争关系,而线程之间是协作关系
进程由主次关系,线程都是平等关系

3. 为什么用线程

  1. 有多个任务需要并发处理
  2. 线程占用资源少,线程适用于任务数非常多的情况;进程占用资源多。
  3. 线程是共有进程内的资源

4. 如何使用多线程

第一种:简单模式

import threading

def task():
    print("子线程 run")

# 与进程不同之处,不需要加main判断,开启线程的代码放哪里都可以
t = threading.Thread(target=task)
t.start()
print("over")

第二种:自定义模式(继承)

import threading

class MyThread(threading.Thread):
    def run(self):
        print("子,run")
        print("子,over")

mt = MyThread()
mt.start()
print("主,over")
"""

根据代码来验证线程的一些功能

第一:验证线程是否能进行并发

import threading
def task():
    while True:
        print("子")
        
t = threading.Thread(target=task)
t.start()

while True:
    print("主")
子
子
子
子
子
子
主
主
主
主
主
主
子
子
子
主
主
...

以上现象,即可说明线程是可以用来并发的执行的。

第二:验证线程和进程的开销问题

if __name__ == '__main__':

    # 线程开销,线程开了1000个
    start_time = time.time()
    for i in range(1000):
        t = threading.Thread()
        t.start()
    print(time.time() - start_time)


    # 进程开销,进程只开了70个
    start_time = time.time()
    for i in range(70):
        p = multiprocessing.Process()
        p.start()

    print(time.time() - start_time)
0.16990160942077637
0.5576796531677246

通过上面的示例,可以看出线程的开销要远远小于进程开销

第三:验证线程数据是否共享

import threading
a = 100

def task():
    global a
    for i in range(100):
        a -= 1

t = threading.Thread(target=task)
t.start()

print(a)
0

可以看出,线程执行时,不会copy主进程的代码再从上自下执行,而是直接从主进程运行的地方开始执行。所以它能共享进程内的资源。

第四:验证多线程的pid是否相同

import threading
import os

def task():
    print(os.getpid())

for i in range(100):
    a = threading.Thread(target=task)
    a.start()

print(f"主  结束,{os.getpid()}")
13600
13600
13600
13600
13600
主  结束,13600

由此可见,多线程的pid是与主线程的pid相同的。

第五:多线程是否会存在争夺资源的安全问题

import threading
import random
import time
a = 10
def task():
    global a
    temp = a
    time.sleep(random.random())
    a = temp -1
    

a_list = []
for i in range(10):
    t = threading.Thread(target=task)
    a_list.append(t)
    t.start()

for i in a_list:
    i.join()

print(a)
9

通过上面的实例,可以发现资源发生了争夺,那么我们应该避免了。

方案一:加锁
import threading
import random
import time

a = 10

s = threading.Lock()        # 创建锁

def task():
    global a
    s.acquire()     # 加锁
    temp = a
    time.sleep(random.random())
    a = temp - 1
    s.release()     # 解锁

a_list = []
for i in range(10):
    t = threading.Thread(target=task)
    a_list.append(t)
    t.start()

for i in a_list:
    i.join()

print(a)
0

这样就解决了争夺资源的问题

第六:验证多线程是否会随着主进程结束而结束

import threading
import time
import random

# 妃子的一生
def task():
    print("妃子 start")
    time.sleep(3)
    print("妃子 over")


# 皇帝的一生
print("皇帝 start")

t = threading.Thread(target=task)
t.start()

print("皇帝 over")
皇帝 start
妃子 start
皇帝 over
妃子 over

这时,你会发现当皇帝over(主进程结束)之后,但是子进程依然没有消失。这种情况并不是我们期望的,我们希望主进程结束后,子进程也会跟随结束,那么对于这样的情况,我们的解决办法是什么了?

解决方案1:给子进程加守护进程
import threading
import time
import random

# 妃子的一生
def task():
    print("妃子 start")
    time.sleep(3)
    print("妃子 over")

# 皇帝的一生
print("皇帝 start")

t = threading.Thread(target=task)
t.daemon = True
t.start()

print("皇帝 over")
皇帝 start
妃子 start
皇帝 over

因此,我们知道了,默认情况下,主线程即使代码执行完毕,也会等待所有子线程(非守护线程)完毕后程序才能结束

多线程常用的属性

import threading
import time

t = threading.Thread()
t.start()
print(t.ident)			# 线程的标识符
   

print(threading.currentThread())        # 打印当前进程对象
t = threading.Thread(target=lambda : print(threading.currentThread()))
t.start()

t1 = threading.Thread(target=lambda : time.sleep(2))
t1.start()

print(threading.enumerate())        # 是一个列表容器,里面存放着所有存活的线程对象
print(threading.active_count())     # 表示存活线程对象的个数
12764
<_MainThread(MainThread, started 8976)>
<Thread(Thread-2, started 12904)>
[<_MainThread(MainThread, started 8976)>, <Thread(Thread-3, started 12780)>]
2
posted @ 2019-07-04 17:41  Hello_Jack  阅读(683)  评论(0编辑  收藏  举报
# 页脚html代码 /*头部导航栏*/ #navigator { font-size:15px; border-bottom: 1px solid #ededed; border-top: 1px solid #ededed; height: 60px;/*导航栏高度,原始50*/ clear: both; margin-top: 25px; } /*导航栏设置,可以自定义导航栏的目录*/ #navList { min-height: 35px; float: left; } #navList li { /*每一个栏目节点*/ float: left; margin: 0 5px 0 0; /*这里原来是0 40px 0 0 */ } #navList a { /*栏目文字的格式*/ display: block; width: 5em; height: 22px; float: left; text-align: center; padding-top: 19px; }