专栏首页大数据入坑指南python自学成才之路 线程间协作之Semaphore,threading.local()

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python自学成才之路 线程间协作 lock,condition,event的使用

    多线程并发时会出现线程安全问题,如果不解决线程并发安全问题可能会让程序出现不可预料的情况。python提供了一些工具包来解决多线程安全问题,下面介绍其中常见的工...

    我是李超人
  • Python自学成才之路 多线程开发

    1.创建线程 Python中提供了threading模块来创建线程,创建方式有两种。

    我是李超人
  • Elasticsearch升级踩坑记之使用snapshot备份数据

    参考文档:https://www.elastic.co/guide/en/elasticsearch/reference/2.4/modules-snapsho...

    我是李超人
  • Android多线程之AsyncTask源码解析

    叶志陈
  • 架构师必备|Hystrix 分布式系统限流、降级、熔断框架

    别问猿哥为啥在PHP技术大全微信公众号中转载非PHP语言体系内的玩意,猿哥只想说:真正的架构师不限于语言,主要是学习架构思想。

    猿哥
  • 线程基础必知必会(二)

    这篇文章将在上篇文章的基础上,进一步讲解线程的相关知识。这篇文章涉及到的知识点有 线程优先级、前台与后台线程、线程参数、lock、Monitor 和 线程异常处...

    喵叔
  • Hystrix 分布式系统限流、降级、熔断框架

    在高并发访问下,这些依赖的稳定性与否对系统的影响非常大,但是依赖有很多不可控问题:如网络连接缓慢,资源繁忙,暂时不可用,服务脱机等,如下图:

    京东技术
  • 【原创】Java并发编程系列2:线程概念与基础操作

    本篇为【Dali王的技术博客】Java并发编程系列第二篇,讲讲有关线程的那些事儿。主要内容是如下这些:

    王金龙
  • java多线程并发控制countDownLatch和cyclicBarrier的使用

    java主线程等待所有子线程执行完毕在执行,这个需求其实我们在工作中经常会用到,比如用户下单一个产品,后台会做一系列的处理,为了提高效率,每个处理都可以用一个线...

    大道七哥
  • 你真的了解AsyncTask?

    虽说现在做网络请求有了Volley全家桶和OkHttp这样好用的库,但是在处理其他后台任务以及与UI交互上,还是需要用到AsyncTask。但是你真的了解Asy...

    weishu

扫码关注云+社区

领取腾讯云代金券