python threading模块、Semaphore类讲解

16.2.5. Semaphore Objects

def Semaphore(*args, **kwargs):
    """A factory function that returns a new semaphore.

    Semaphores manage a counter representing the number of release() calls minus
    the number of acquire() calls, plus an initial value. The acquire() method
    blocks if necessary until it can return without making the counter
    negative. If not given, value defaults to 1.

    """
    return _Semaphore(*args, **kwargs)

翻译: Semaphore是一个工厂函数,负责返回一个新的信号量对象。Semaphore内部维护一个计数器,该计数器代表 初始值+release-acquare的值。每次调用acquare方法都会使内部计数器减一,一旦计数器为负的,则acquare方法会被阻塞,直到其他线程调用release方法,使信号量内部计数器值为正。 计数器初始值为1。

真正的信号量类:_Semaphore

class _Semaphore(_Verbose):
    """Semaphores manage a counter representing the number of release() calls
       minus the number of acquire() calls, plus an initial value. The acquire()
       method blocks if necessary until it can return without making the counter
       negative. If not given, value defaults to 1.

    """

    # After Tim Peters' semaphore class, but not quite the same (no maximum)

    def __init__(self, value=1, verbose=None): #构造函数
        if value < 0:
            raise ValueError("semaphore initial value must be >= 0") #计数器初始值必须是非负的
        _Verbose.__init__(self, verbose)
        self.__cond = Condition(Lock()) #内部条件变量
        self.__value = value #内部计数器

    def acquire(self, blocking=1):
        """Acquire a semaphore, decrementing the internal counter by one.

        When invoked without arguments: if the internal counter is larger than
        zero on entry, decrement it by one and return immediately. If it is zero
        on entry, block, waiting until some other thread has called release() to
        make it larger than zero. This is done with proper interlocking so that
        if multiple acquire() calls are blocked, release() will wake exactly one
        of them up. The implementation may pick one at random, so the order in
        which blocked threads are awakened should not be relied on. There is no
        return value in this case.

        When invoked with blocking set to true, do the same thing as when called
        without arguments, and return true.

        When invoked with blocking set to false, do not block. If a call without
        an argument would block, return false immediately; otherwise, do the
        same thing as when called without arguments, and return true.

        """
        rc = False
        with self.__cond: #获取条件变量,会自动释放内部锁
            while self.__value == 0: #内部计数器值为0。阻塞模式则会等待通知;非阻塞模式则直接退出循环
                if not blocking:
                    break
                if __debug__:
                    self._note("%s.acquire(%s): blocked waiting, value=%s",
                            self, blocking, self.__value)
                self.__cond.wait() #释放内部锁,等待通知唤醒
            else:
                self.__value = self.__value - 1
                if __debug__:
                    self._note("%s.acquire: success, value=%s",
                            self, self.__value)
                rc = True
        return rc

    __enter__ = acquire

    def release(self):
        """Release a semaphore, incrementing the internal counter by one.

        When the counter is zero on entry and another thread is waiting for it
        to become larger than zero again, wake up that thread.

        """
        with self.__cond: #获取条件变量,会自动释放内部锁
            self.__value = self.__value + 1 #内部计数器值加1
            if __debug__:
                self._note("%s.release: success, value=%s",
                        self, self.__value)
            self.__cond.notify() #唤醒其他等待线程

    def __exit__(self, t, v, tb):
        self.release()

**提示:**信号量主要用在保护有限的资源。以数据库连接数为例说明,假设当前数据库支持最大连接数为3,将信号量初始值设为3,那么同时最大可以有三个线程连接数据库,其他线程若再想连接数据库,则只有等待,直到某一个线程释放数据库连接。
演示:

import threading
import time

sm=threading.Semaphore(3)

def connectDb():
    sm.acquire()

    print threading.currentThread().getName()+' connecting to db...\n'

    time.sleep(2)

    print threading.currentThread().getName()+' released db...\n'

    sm.release()


if __name__ == '__main__':
    s1=threading.Thread(target=connectDb,args=())
    s2 = threading.Thread(target=connectDb, args=())
    s3 = threading.Thread(target=connectDb, args=())
    s4 = threading.Thread(target=connectDb, args=())

    s1.start()
    s2.start()
    s3.start()
    s4.start()

运行结果:

C:\Python27\python.exe E:/pythonproj/基础练习/t8.py
Thread-1 connecting to db...

Thread-2 connecting to db...

Thread-3 connecting to db...

Thread-1 released db...

Thread-2 released db...
Thread-3 released db...


Thread-4 connecting to db...

Thread-4 released db...


Process finished with exit code 0

你可能感兴趣的:(python)