Python多线程编程(二)之多线程同步

在多线程环境下,如果多个线程同时对于某个数据进行修改,则可能出现不可预料的后果,为了保证数据被正确修改。就需要对多个线程进行同步。最经典的就是生产者消费者模型。

生产者消费者问题

生产者消费者问题,是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

这里写图片描述

要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常采用进程间通信的方法解决该问题,常用的方法有信号灯法等。如果解决方法不够完善,则容易出现活锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。该问题也能被推广到多个生产者和消费者的情形。

生产者消费者的优点:

  • 解耦:
    假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化,可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。
  • 并发
    由于生产者与消费者是两个独立的并发体,他们之间是用缓冲区通信的,生产者只需要往缓冲区里丢数据,就可以继续生产下一个数据,而消费者只需要从缓冲区拿数据即可,这样就不会因为彼此的处理速度而发生阻塞。
  • 支持忙闲不均
    缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。

  • -

多线程同步

简单线程同步

使用Thread对象的Lock和RLock可以实现简单的线程同步。Lock和RLock对象都具有acquire和release方法。如果某一数据在某一时刻只允许一个线程进行操作,则可以将操作过程放在acquire和release方法中间。说简单同步的原因是如果一个ziyuan,下面是实现的一个简单的线程同步:

import threading
import time

class Mythread(threading.Thread):

    def __init__(self,threadname):
        threading.Thread.__init__(self,name=threadname)#调用Thread类的构造函数

    def run(self):
        global x
        lock.acquire()
        for i in range(3):
            x=x+1
        time.sleep(1)
        print(x)
        lock.release()

lock=threading.Lock()
t1=[]
for i in range(10):
    t=Mythread(str(i))
    t1.append(t)

x=0
for i in t1:
    i.start()

这里写图片描述
在上述代码中创建了10个线程,每个线程都对全局变量x进行操作,然后打印x。每个线程对x进行的操作是对x+3。结果如上图所示。若将acquire和release删除了,最后结果为下图。其原因是在第一个线程执行完+3操作之后,还没来得及打印,就先休眠了。然后另一个线程接着访问x。以此类对。直到所有的线程多执行完x=x+3操作之后,然后再执行打印操作。此时x已经变为了30

这里写图片描述

使用条件变量保持同步

Python的Condition对象提供对复杂线程同步的支持。使用condition对象可以在某些事件之后才触发对数据的处理。下面是经典的生产者消费者问题。

import threading

class Producer(threading.Thread):

    def __init__(self,threadname):
        threading.Thread.__init__(self,name=threadname)

    def run(self):
        global x
        con.acquire()
        if x==10000:
            con.wait()
            pass
        else:
            for i in range(10000):
                x+=1
                con.notify()
        print(x)
        con.release()

class Consumer(threading.Thread):

    def __init__(self,threadname):
        threading.Thread.__init__(self,name=threadname)

    def run(self):
        global x
        con.acquire()
        if x ==0:
            con.wait()
            pass
        else:
            for i in range(10000):
                x=x-1
                con.notify()
        print(x)
        con.release()

con=threading.Condition()
x=0
p=Producer('Producer')
c=Consumer('Consumer')
p.start()
c.start()
p.join()
c.join()

使用queue实现线程同步

Python的Queue模块提供一种适用于多线程编程的FIFO实现。它可用于在生产者(producer)和消费者(consumer)之间线程安全(thread-safe)地传递消息或其它数据,因此多个线程可以共用同一个Queue实例。Queue的大小(元素的个数)可用来限制内存的使用。并且Queue模块已经实现了同步与互斥操作。所以可以实现一对一的生产者消费者模式,也可实现多对多的·生产者消费者模式。

使用继承Thread类实现生产者消费者模式:

扫描二维码关注公众号,回复: 1114722 查看本文章
from queue import Queue
import random,threading,time

#生产者类
class Producer(threading.Thread):
    def __init__(self, name,queue):
        threading.Thread.__init__(self, name=name)
        self.data=queue

    def run(self):
        for i in range(5):
            print("%s is producing %d to the queue!" % (self.getName(), i))
            self.data.put(i)
            time.sleep(random.randrange(2))
        print("%s finished!" % self.getName())

#消费者类
class Consumer(threading.Thread):
    def __init__(self,name,queue):
        threading.Thread.__init__(self,name=name)
        self.data=queue
    def run(self):
        for i in range(5):
            val = self.data.get()
            print("%s is consuming. %d in the queue is consumed!" % (self.getName(),val))
            time.sleep(random.randrange(2))
        print("%s finished!" % self.getName())

def main():
    queue = Queue()
    producer = Producer('Producer',queue)
    consumer = Consumer('Consumer',queue)
    producer.start()
    consumer.start()
    producer.join()
    consumer.join()
    print('All threads finished!')

if __name__ == '__main__':
    main()

下面利用函数实现生产者消费者模式


import queue
import threading
import time,random

def producer():
    for i in range(10):
        print("producer put %d to queue"%i)
        data.put(i)
        time.sleep(random.randrange(1))
    print("Producer is finished")

def consumer():
    for i in range(10):
        print("consumer get %d from queue"%data.get())
        time.sleep(random.randrange(1))
    print("Consumer is finished")

data=queue.Queue()
t1=threading.Thread(target=producer,name="Producer")
t2=threading.Thread(target=consumer,name="Consumer")
t1.start()
t2.start()
t1.join()
t2.join()

最后的结果
这里写图片描述

折腾了大半天总算把python的多线程编程以及生产者消费者模型搞明白o(╯□╰)o

猜你喜欢

转载自blog.csdn.net/gzj_1101/article/details/79740757