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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gzj_1101/article/details/79740757

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

生产者消费者问题

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

这里写图片描述

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

生产者消费者的优点:

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

  • -

多线程同步

简单线程同步

使用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()

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

这里写图片描述
在上述代码中创建了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()
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

使用queue实现线程同步

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

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

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()
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

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


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()

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

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

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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值