Simple code demonstration of semaphore semaphore semaphore
import threading import logging import time FORMAT = '%(threadName)s %(thread)d %(message)s' logging.basicConfig(format=FORMAT, level=logging.INFO) def worker(s:threading.Semaphore): logging.info('in sub thread') logging.info(s.acquire()) # Get semaphore, counter-1 logging.info('sub thread over') s = threading.Semaphore(3) # Create 3 semaphore counters logging.info(s.acquire()) print(s._value) # Let's see what's in the semaphore now logging.info(s.acquire()) print(s._value) logging.info(s.acquire()) print(s._value) threading.Thread(target=worker, args=(s, )).start() time.sleep(2) logging.info(s.acquire(False)) # No blocking, False if no semaphore is available logging.info(s.acquire(timeout=10)) # Set the time-out time. After the time-out time, no signal has been obtained. The return value is False # release logging.info('released') s.release() # Release semaphore, counter i + 1
Simple resource pool demonstration
import threading import logging import random FORMAT = '%(threadName)s %(thread)d %(message)s' logging.basicConfig(format=FORMAT, level=logging.INFO) class Conn: def __init__(self, name): self.name = name def __str__(self): return self.name class Pool: def __init__(self, count:int): self.count = count self.pool = [ self._connect('conn-{}'.format(x)) for x in range(self.count)] def _connect(self, conn_name): return Conn(conn_name) def get_conn(self): conn = self.pool.pop() return conn def return_conn(self, conn:Conn): self.pool.append(conn) pool = Pool(3) def worker(pool:Pool): conn = pool.get_conn() logging.info(conn) threading.Event().wait(random.randint(1,4)) pool.return_conn(conn) for i in range(6): threading.Thread(target=worker, name='worker-{}'.format(i), args=(pool,)).start()
Using semaphore to refine the code
import threading import logging import random FORMAT = '%(threadName)s %(thread)d %(message)s' logging.basicConfig(format=FORMAT, level=logging.INFO) class Conn: def __init__(self, name): self.name = name def __str__(self): return self.name class Pool: def __init__(self, count:int): self.count = count self.pool = [ self._connect('conn-{}'.format(x)) for x in range(self.count)] self.semahore = threading.Semaphore(count) def _connect(self, conn_name): return Conn(conn_name) def get_conn(self): self.semahore.acquire() conn = self.pool.pop() return conn def return_conn(self, conn:Conn): self.pool.append(conn) self.semahore.release() pool = Pool(3) def worker(pool:Pool): conn = pool.get_conn() logging.info(conn) threading.Event().wait(random.randint(1,4)) pool.return_conn(conn) for i in range(6): threading.Thread(target=worker, name='worker-{}'.format(i), args=(pool,)).start()
On the problem that the semaphore release exceeds the range of initial value
When we use semaphore, if we haven't yet acquire d it, we will release it. What's the problem?
The value of the generated semaphore is + 1, which exceeds the initial value of semaphore. The following example shows this problem
import threading import logging FORMAT = '%(threadName)s %(thread)d %(message)s' logging.basicConfig(format=FORMAT, level=logging.INFO) sema = threading.Semaphore(3) logging.warning(sema.__dict__) for _ in range(3): sema.acquire() logging.warning('-----') logging.warning(sema.__dict__) for _ in range(4): sema.release() logging.warning(sema.__dict__) for _ in range(3): sema.acquire() logging.warning('--------') logging.warning(sema.__dict__) sema.acquire() logging.warning('======') logging.warning(sema.__dict__)
Therefore, the BoundedSemaphore class can be used to implement the bounded semaphore. If the release exceeds the range of the initial value, a ValueError exception will be thrown