前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python自学成才之路 线程间协作之Semaphore,threading.local()

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

作者头像
我是李超人
发布2020-09-08 15:05:23
7890
发布2020-09-08 15:05:23
举报

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

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

代码语言:javascript
复制
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内部的计数器大于初始值时会报错。

代码语言:javascript
复制
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()都能创建属于当前线程特有的属性。举个简单的栗子:

代码语言:javascript
复制
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里面,由于这个映射的存在,每个线程能且只能访问自己添加的属性。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-09-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档