python自学成才之路 线程间协作之Semaphore,threading.local()

信号量
信号量用来控制线程并发数的,信号量里面维护了一个计数器,这个计数器可以理解为锁的数量,线程通过acquire方法去申请锁,每申请到一个锁,计数器就减1。线程通过release释放锁,每释放一个锁,计数器就加1。当计数器为0的时候,通过acquire方法去申请锁会被阻塞,直到有其它的线程释放锁让计数器不为0才有可能申请到锁。

信号量有两种BoundedSemaphore或Semaphore,用Semaphore举个栗子:


import threading, time


class myThread(threading.Thread):

    def run(self):
        semaphore.acquire()
        print(threading.currentThread().name + " 获得锁")
        time.sleep(1)
        print(threading.currentThread().name + " 释放锁")
        semaphore.release()


if __name__ == "__main__":
    semaphore = threading.Semaphore(2)
    for i in range(6):
        myThread().start()
输出:
Thread-1 获得锁
Thread-2 获得锁
Thread-1 释放锁
Thread-2 释放锁
Thread-3 获得锁
Thread-4 获得锁
Thread-4 释放锁
Thread-5 获得锁
Thread-3 释放锁
Thread-6 获得锁
Thread-6 释放锁
Thread-5 释放锁

BoundedSemaphore或Semaphore的用法几乎是一样的,这两个信号量有什么区别呢?要想明白这两个信号量的区别,得先弄明白release这个方法。其实任何一个线程都可以调用release方法,即使这个线程没有获取过锁,并且一个线程可以多次调用release,任意一个线程调用release方法都是有效的。前面说过线程每调用一次release方法,信号量内部的计数器都会加1,所以会出现由于线程调用release次数过多,导致计数器的值大于信号量计数器的初始值。Semaphore对内部的计数器是没有限制的,但是BoundedSemaphore有限制,BoundedSemaphore内部的计数器大于初始值时会报错。


class MyThread(threading.Thread):

    def run(self):
        # semaphore.acquire()
        # print(threading.currentThread().name + " 获得锁")
        print(threading.currentThread().name + " 释放锁")
        # 连续释放三次锁
        semaphore.release()
        semaphore.release()
        semaphore.release()


class MyAcquire(threading.Thread):

    def run(self):
        semaphore.acquire()
        time.sleep(5)
        print(threading.currentThread().name + " 获得锁")

if __name__ == "__main__":
    semaphore = threading.Semaphore(1)
    MyThread().start()

    for i in range(4):
        MyAcquire().start()

输出:
Thread-1 释放锁
Thread-2 获得锁
Thread-5 获得锁
Thread-4 获得锁
Thread-3 获得锁

上面这个案例中,使用Semaphore信号量,一个线程多次释放锁,使得其它几个线程都能获取到锁。如果将Semaphore改成BoundedSemaphore,这个程序就会报错,因为BoundedSemaphore设置的计数器初始值是1,连续三次释放信号量肯定会使计数器的值大于1,而BoundedSemaphore是不允许计数器的值大于初始值,所以会抛出异常。程序里面是为了演示效果,所以让一个线程多次释放,实际使用的时候不要这么做,最好是线程获取一次信号量再释放一次信号量。

threading.local()
threading.local()是一个全局对象,每个线程使用threading.local()都能创建属于当前线程特有的属性。举个简单的栗子:

import threading

a = threading.local()

def worker():
    a.x = 0
    a.x += 1
    print(threading.currentThread().name, a.x)


for i in range(3):
    threading.Thread(target=worker).start()
输出:
Thread-1 1
Thread-2 1
Thread-3 1

输出:
AttributeError: '_thread._local' object has no attribute 'y'

上面这个例子中加了一个a.y属性,这个属性只属于主线程,所以再其它线程中访问a.y的时候就报错了(AttributeError: ‘_thread._local’ object has no attribute ‘y’)。这进一步说明每个线程可以在threading.local()里面添加属于当前线程的特有属性,这些属性对其它线程是不可见的。

这是怎么实现的呢?其实Threading.local()内部维护了一个(key,dict)这么一个映射,每个线程在使用threading.local()时都会分配一个key,线程添加的属性都会存在dict里面,由于这个映射的存在,每个线程能且只能访问自己添加的属性。



本人是做大数据开发的,在微信上开了个个人号,会经常在上面分享一些学习心得,原创文章都会首发到公众号上,感兴趣的盆友可以关注下哦!
python自学成才之路 线程间协作之Semaphore,threading.local()_第1张图片
备注:微信公众号搜索‘大数据入坑指南

你可能感兴趣的:(python)