基于python下的多线程(经典生产者消费者问题)

多线程能干什么:

生产者消费者问题:(经典)
一直生产 一直消费 中间有阀值 避免供求关系不平衡
#线程安全问题,要是线程同时来,听谁的
#锁:一种数据结构 队列:先进线出 栈:先进后出
#生产者消费者的优点(为什么经典的设计模式)
1.解耦(让程序各模块之间的关联性降到最低)
    假设生产者和消费者是两个类,如果让生产者直接调用消费者的某个方法,
那么生产者对于消费者就会产生依赖(也就是耦合),于某个缓冲区,两者之间
不直接依赖,耦合也就相应降低了。
生活中的例子:我们 邮筒 邮递员:
举个例子,我们去邮局投递信件,如果不使用邮筒(也就是缓冲区),你必须得
把信直接交给邮递员,有同学会说,直接交给邮递员不是挺简单的嘛,其实不
简单,你必须得认识邮递员,才能把信给他,光凭身上的制服,万一有人假冒呢?
这就产成你和邮递员之间的依赖了(相当于生产者消费者强耦合),万一哪天
邮递员换人了,你还要重新认识一下(相当于消费者变化导致修改生产者代码),
而邮筒相对来说比较固定,你依赖它的成本就比较低(相当于和缓冲区之间的弱耦合)
2.支持并发:
    生产者消费者是两个独立的并发体,他们之间是用缓冲区作为桥梁连接,
者只需要从缓冲区里拿数据即可,这样就不会因为彼此速度而发生阻塞。
接着上面的例子:如果我们不使用邮筒,我们就得在邮局等邮递员,直到他
回来了,我们才能把信给他,这期间我们啥也不能干(也就是产生阻塞),
或者邮递员挨家挨户的问(产生论寻)
3.支持忙闲不均
    如果制造数据的速度时快时慢,缓冲区的好处就体现出来了,当数据
制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中,
等生产者的速度慢下来,消费者再慢慢处理情人节信件太多了,邮递员一次
处理不了,可以放在邮筒中,下次再来取。

一个简单的多任务问题:

从执行结果看到,只会在一个任务完成了之后才会进行下一个任务,效率低下

from time import ctime, sleep
def music(a):
    for i in range(2):
        print 'I was listening to %s. %s' % (a, ctime())
        sleep(2)
def movie(b):
    for i in range(2):
        print 'I was listening to %s. %s' % (b, ctime())
        sleep(5)
music('告白气球')
movie('泰坦尼克号')
print 'all over %s' % ctime()

这里写图片描述

将多任务问题用多线程解决,提高效率:

from threading import Thread
from time import ctime, sleep
def music(a):
    for i in range(2):
        print 'I was listening to %s. %s' % (a, ctime())
        sleep(2)
def movie(b):
    for i in range(2):
        print 'I was listening to %s. %s' % (b, ctime())
        sleep(5)
t1 = Thread(target=music, args=('告白气球',))
t1.start()
t2 = Thread(target=movie, args=('泰坦尼克号',))
t2.start()
print 'all over %s' % ctime()

这里写图片描述

线程的定义:

线程是操作系统能够进行运算调度的最小单位(程序执行流的最小单元)
它被包括在进程之中,是进程的实际运作单位。一个进程可以并发多个线程
每条线程并行执行不同的任务
(线程是进程的一个实体,是被系统独立调度和分派的基本单元)
每一个进程启动时都会最先产生一个线程,即主线程
然后主线程会再创建其他的子线程

通常,我们写服务器处理模型的程序时,有以下几种模型:

(1)每收到一个请求,创建一个新的进程,来处理该请求;
(2)每收到一个请求,创建一个新的线程,来处理该请求;
(3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求
上面的几种方式,各有千秋,
第(1)中方法,由于创建新的进程的开销比较大,所以,会导致服务器性能比较差,但实现比较简单。
第(2)种方式,由于要涉及到线程的同步,有可能会面临死锁等问题。
第(3)种方式,在写应用程序代码时,逻辑比前面两种都复杂。
综合考虑各方面因素,一般普遍认为第(3)种方式是大多数网络服务器采用的方式

线程的缺点:

在UI编程中,常常要对鼠标点击进行相应,首先如何获得鼠标点击呢?
方式一:创建一个线程,该线程一直循环检测是否有鼠标点击,
那么这个方式有以下几个缺点:
CPU资源浪费,可能鼠标点击的频率非常小,但是扫描线程还是会一直循环检测,
这会造成很多的CPU资源浪费;如果扫描鼠标点击的接口是阻塞的呢?
如果是堵塞的,又会出现下面这样的问题,如果我们不但要扫描鼠标点击,
还要扫描键盘是否按下,由于扫描鼠标时被堵塞了,那么可能永远不会去扫描键盘;
如果一个循环需要扫描的设备非常多,这又会引来响应时间的问题;
所以,该方式是非常不好的。

多线程01:

from threading import Thread


def Foo(arg):
    print arg

print 'before'
# 线程和函数建立关系
t1 = Thread(target=Foo, args=(1,))
t1.start()
print t1.getName()
print 'after'

这里写图片描述

多线程02:

from threading import Thread
import time


def Foo():
    for item in range(11):
        print item
        time.sleep(2)
print 'before'
t1 = Thread(target=Foo)
t1.start()
print 'after'

多线程02升级版(加入了t1.setDaemon(True)):

意味着主线程不需要等待子线程啥时候结束就可以结束 但是会等到子线程结束之后一起销毁

from threading import Thread
import time


def Foo():
    for item in range(11):
        print item
        time.sleep(2)
print 'before'
t1 = Thread(target=Foo)
t1.setDaemon(True)
t1.start()
print 'after'
time.sleep(10)

这里写图片描述

多线程03:加入了t1.join(5),表示主线程到join()就不往下走了,直到子线程进行完成

from threading import Thread
import time


def Foo():
    for item in range(10):
        print item
        time.sleep(1)
print 'before'
t1 = Thread(target=Foo)
t1.start()
# 主线程到join()就不往下走了,直到子线程进行完成
t1.join(5)
# 这句代码意味着主线程主会等待子线程5秒之后就结束了,不管子线程是否完成
print 'after'

这里写图片描述

操作系统(生产者消费者问题) 但是这段代码没有交互,不是动态的

import threading
import Queue
import time


def Producer(name, que):
    que.put('baozi')
    print '%s:Made a baozi...' % name
def Consumer(name,que):
    que.get()
    print '%s:Got a baozi...' % name
# 创建队列
q = Queue.Queue()
p1 = threading.Thread(target=Producer, args=['chef1', q])
p2 = threading.Thread(target=Producer, args=['chef2', q])
p1.start()
p2.start()
c1 = threading.Thread(target=Consumer, args=['tom', q])
c2 = threading.Thread(target=Consumer, args=['harry', q])
c1.start()
c2.start()

这里写图片描述

操作系统升级版01:实现了动态化

import threading
import Queue
import time
import random


def Producer(name, que):
    while True:
        que.put('baozi')
        print '%s:Made a baozi...' % name
        time.sleep (random.randrange(3))


def Consumer(name, que):
    while True:
        que.get()
        print '%s:Got a baozi...' % name
        time.sleep(random.randrange(3))
# 创建队列
q = Queue.Queue()
p1 = threading.Thread(target=Producer, args=['chef1', q])
p2 = threading.Thread(target=Producer, args=['chef2', q])
p1.start()
p2.start()
c1 = threading.Thread(target=Consumer, args=['tom', q])
c2 = threading.Thread(target=Consumer, args=['harry', q])
c1.start()
c2.start()

这里写图片描述

操作系统升级版02版加入了que.get_nowait()加入消费者不等待会报错

import threading
import Queue
import time
import random


def Producer(name, que):
    while True:
        que.put('baozi')
        print '%s:Made a baozi...' % name
        time.sleep (random.randrange(3))


def Consumer(name, que):
    while True:
        que.get_nowait()
        print '%s:Got a baozi...' % name
        time.sleep(random.randrange(3))
# 创建队列
q = Queue.Queue()
p1 = threading.Thread(target=Producer, args=['chef1', q])
p2 = threading.Thread(target=Producer, args=['chef2', q])
p1.start()
p2.start()
c1 = threading.Thread(target=Consumer, args=['tom', q])
c2 = threading.Thread(target=Consumer, args=['harry', q])
c1.start()
c2.start()

这里写图片描述

操作系统升级版03版“做包子时间大于吃包子时间”会报错加入捕获异常

import threading
import Queue
import time
import random


def Producer(name, que):
    while True:
        que.put('baozi')
        print '%s:Made a baozi...' % name
        time.sleep (random.randrange(5))


def Consumer(name, que):
    while True:
        try:
            que.get_nowait()
            print '%s:Got a baozi...' % name
        except Exception:
            print '没有包子了'
        time.sleep(random.randrange(3))
# 创建队列
q = Queue.Queue()
p1 = threading.Thread(target=Producer, args=['chef1', q])
p2 = threading.Thread(target=Producer, args=['chef2', q])
p1.start()
p2.start()
c1 = threading.Thread(target=Consumer, args=['tom', q])
c2 = threading.Thread(target=Consumer, args=['harry', q])
c1.start()
c2.start()

会有没有包子的提示:
这里写图片描述

操作系统升级版04:实现智能化,生产者会合理的生产,消费者会等待,实现合理化

import threading
import Queue
import time
import random


def Producer(name, que):
    while True:
        if que.qsize() < 3:
            que.put('baozi')
            print '%s:Made a baozi...' % name
        else:
            print '还有三个包子'
        time.sleep (random.randrange(5))


def Consumer(name, que):
    while True:
        try:
            que.get_nowait()
            print '%s:Got a baozi...' % name
        except Exception:
            print '没有包子了'
        time.sleep(random.randrange(3))
# 创建队列
q = Queue.Queue()
p1 = threading.Thread(target=Producer, args=['chef1', q])
p2 = threading.Thread(target=Producer, args=['chef2', q])
p1.start()
p2.start()
c1 = threading.Thread(target=Consumer, args=['tom', q])
c2 = threading.Thread(target=Consumer, args=['harry', q])
c1.start()
c2.start()

这里写图片描述

异步的方式:模拟生活中作的事,“生产者和消费者有更加智能的交互”

import threading
import time

def Producer():
    print 'chef:等人来买包子'
    # 收到了消费者的event.set 也就是把这个flag改为了true,但是我们的包子并没有做好
    event.wait()
    # 此时应该将flag的值改回去
    event.clear()
    print 'chef:someone is coming for 包子'
    print 'chef:making a 包子 for someone'
    time.sleep(5)
    # 告诉人家包子做好了
    print '你的包子好了~'
    event.set()

def Consumer():
    print 'tom:去买包子'
    # 告诉人家我来了
    event.set()
    time.sleep(2)
    print 'tom:waiting for 包子 to be ready'
    # 我在不断检测,但我已经不阻塞了
    while True:
        if event.is_set():
            print 'Thanks~'
            break
        else:
            print '怎么还没好呀~'
            # 模拟正在做自己的事情
            time.sleep(1)
event = threading.Event()

p1 = threading.Thread(target=Producer)
c1 = threading.Thread(target=Consumer)
p1.start()
c1.start()

这里写图片描述

事件驱动模型:

事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。
它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发
相应的处理。另外两种常见的编程范式是(单线程)同步以及多线程编程。
目前大部分的UI编程都是事件驱动模型,如很多UI平台都会提供onClick()
事件,这个事件就代表鼠标按下事件。事件驱动模型大体思路如下:
1. 有一个事件(消息)队列;
2. 鼠标按下时,往这个队列中增加一个点击事件(消息);
3. 有个循环,不断从队列取出事件,根据不同的事件,调用不同的函数,
如onClick()、onKeyDown()等;
4. 事件(消息)一般都各自保存各自的处理函数指针,这样,每个消息都有独立的处理函数;

事件驱动方式更智能化的让生产者和消费者交互:

import threading
import time

def Producer():
    print 'chef:等人来买包子'
    #收到了消费者的event.set 也就是把这个flag改为了true,但是我们的包子并没有做好
    event.wait()
    #此时应该将flag的值改回去
    event.clear()
    print 'chef:someone is coming for 包子'
    print 'chef:making a 包子 for someone'
    time.sleep(5)
    # 告诉人家包子做好了
    print '你的包子好了~'
    event.set()

def Consumer():
    print 'tom:去买包子'
    # 告诉人家我来了
    event.set()
    time.sleep(2)
    print 'tom:waiting for 包子 to be ready'
    event.wait()
    print '哎呀~真好吃'

event = threading.Event()

p1 = threading.Thread(target=Producer)
c1 = threading.Thread(target=Consumer)
p1.start()
c1.start()

这里写图片描述

线程安全问题: 线程会出现异常无法输出正确的结果

import threading
import time
num = 0


def run(n):
    time.sleep(1)
    global num
    num += 1
    print '%s\n' % num

for i in range(1500):
    t = threading.Thread(target=run,args=(i,))
    t.start()

这里写图片描述

给线程加锁可以使得正确的输出

import threading
import time
num = 0


def run(n):
    time.sleep(1)
    global num
    lock.acquire()
    num += 1
    print '%s\n' % num
    lock.release()
lock = threading.Lock()
for i in range(1500):
    t = threading.Thread(target=run,args=(i,))
    t.start()

这里写图片描述

猜你喜欢

转载自blog.csdn.net/aaaaaab_/article/details/81216921