zoukankan      html  css  js  c++  java
  • python--线程同步原语

    Threading模块是python3里面的多线程模块,模块内集成了许多的类,其中包括Thread,Condition,Event,Lock,Rlock,Semaphore,Timer等等。下面这篇文章主要通过案例来说明其中的Event和Segmaphore(Boundedsegmaphore)的使用。关于Lock的使用可以移步到我之前写的文章python同步原语--线程锁。

    Event

    Event类内部保存着一个flags参数,标志事件的等待与否。

    Event类实例函数

    1. set() 将flags设置为True,事件停止阻塞

    2. clear()  将flags重新设置为False,删除flags,事件重新阻塞

    3. wait() 将事件设置为等待状态

    4.is_set()判断flags是否被设置,如果被设置返回True,否则返回False

    (1)单个事件等待其他事件的发生

     具体代码:

    from time import ctime,sleep
    event = Event()
    
    def event_wait():
        print(ctime())
        event.wait()
        print('这是event_wait方法中的时间',ctime())
    
    def event_set(n):
        sleep(n)
        event.set()
        print('这是event_set方法中的时间', ctime())
    
    thread1 = Thread(target=event_wait)
    thread2 = Thread(target=event_set,args=(3,))
    
    thread1.start()
    thread2.start()

    结果:

    Sat Nov 17 10:01:05 2018
    这是event_wait方法中的时间 Sat Nov 17 10:01:08 2018
    这是event_set方法中的时间  Sat Nov 17 10:01:08 2018

    (2)多个事件先后发生

    下面以赛跑来作为例子。假设5条跑道上,每条跑道各有一名运动员,分别为ABCDE。

    具体代码:

    from threading import Event
    from  threading import Thread
    import threading
    
    event = Event()
    
    def do_wait(athlete):
        racetrack = threading.current_thread().getName()
        print('%s准备就绪' % racetrack)
        event.wait()
        print('%s听到枪声,起跑!'%athlete)
    
    thread1 = Thread(target=do_wait,args=("A",))
    thread2 = Thread(target=do_wait,args=("B",))
    thread3 = Thread(target=do_wait,args=("C",))
    thread4 = Thread(target=do_wait,args=("D",))
    thread5 = Thread(target=do_wait,args=("E",))
    
    threads = []
    threads.append(thread1)
    threads.append(thread2)
    threads.append(thread3)
    threads.append(thread4)
    threads.append(thread5)
    
    for th in threads:
        th.start()
    
    event.set()  #这个set()方法很关键,同时对5个线程中的event进行set操作

    结果:

    Thread-1准备就绪
    Thread-2准备就绪
    Thread-3准备就绪
    Thread-4准备就绪
    Thread-5准备就绪
    E听到枪声,起跑!
    A听到枪声,起跑!
    B听到枪声,起跑!
    D听到枪声,起跑!
    C听到枪声,起跑!

    可以看出多个线程中event的set()是随机的,其内部的实现是因为一个notify_all()方法。这个方法会一次性释放所有锁住的事件,哪个线程先抢到线程运行的时间片,就先释放锁。

    之所以能够只调用一个set()函数就可以实现所有event的退出阻塞,是因为event.wait()是在线程内部实现的,而set()函数是在进程中调用,python多线程共享一个进程内存空间。如果是在不同进程中调用这两个函数则无法实现。

    BoundedSegmaphore

    如果在主机执行IO密集型任务的时候再执行这种短时间内完成大量任务(多线程)的程序时,计算机就有很大可能会宕机。

    这时候就可以为这段程序添加一个计数器(counter)功能,来限制一个时间点内的线程数量。当每次进行IO操作时,都需要向segmaphore请求资源(锁),如果没有请求到,就阻塞等待,请求成功才就像执行任务。

    BoundedSegmaphore和Segmaphore的区别

    BoundedSegmaphore请求的锁数量固定为传入参数,而Segmaphore请求的锁数量可以超过传入的参数。

    主要函数:

    1. acquire()  请求锁

    2. release()   释放锁

    下面以一个租房的例子来说明这种固定锁数量的机制。假设一家小公寓有6间房,原本有2个住户在住着。

    具体代码实现:

    from threading import BoundedSemaphore,Lock,Thread
    from time import sleep
    from random import randrange
    
    lock = Lock()
    num = 6
    hotel = BoundedSemaphore(num)
    
    def logout():
        lock.acquire()
        print('I want to logout')
        print('A customer logout...')
        try:
            hotel.release()
            print('Welcome again')
        except ValueError:
            print('Sorry,wait a moment.')
        lock.release()
    
    def login():
        lock.acquire()
        print('I want to login')
        print('A customer login...')
        if hotel.acquire(False):
            print('Ok,your room number is...')
        else:
            print('Sorry,our hotel is full')
        lock.release()
    
    #房东
    def producer(loops):
        for i in range(loops):
            logout()
            print('还剩%s' % hotel._value, '房间')
            sleep(randrange(2))
    #租客
    def consumer(loops):
        for i in range(loops):
            login()
            print('还剩%s' % hotel._value, '房间')
            sleep(randrange(2))
    def main():
        print('Start')
        room_num = hotel._value
        print('The hotel is full with %s room'%room_num)
        #原本有2个住户
        hotel.acquire()
        hotel.acquire()
        thread1 = Thread(target=producer,args=(randrange(2,8),))
        thread2 = Thread(target=consumer,args=(randrange(2,8),))
        thread1.start()
        thread2.start()
    
    if __name__ == '__main__':
        main()

    结果:

    The hotel is full with 6 room
    I want to logout
    A customer logout...
    Welcome again
    还剩5 房间
    I want to logout
    A customer logout...
    Welcome again
    还剩6 房间
    I want to login
    A customer login...
    Ok,your room number is...
    还剩5 房间
    I want to login
    A customer login...
    Ok,your room number is...
    还剩4 房间

    可以看出,房间数目永远不会超过6,因为_value值(BoundedSegmaphore内部的计数器counter)一定是传入的参数6。

  • 相关阅读:
    MySQL 编码:utf8 与 utf8mb4,utf8mb4_unicode_ci 与 utf8mb4_general_ci
    用 Redis 实现 PHP 的简单消息队列
    C 语言跟 C++ 的差异比较
    Redis -主从复制
    Redis
    Linux 下在后台运行进程:nohup,setsid,& 以及 tmux
    Linux 下的分屏利器-tmux安装、原理及使用
    理财型保险-不值得购买的保险
    常见的保险产品类别-年金保险、意外险
    签订保险合同后的事-续保、批单、退保、理赔
  • 原文地址:https://www.cnblogs.com/thomson-fred/p/9986802.html
Copyright © 2011-2022 走看看