###
15.3.4.1 `Semaphore` Example

Semaphores are often used to guard resources with limited capacity, for example, a database server. In any situation where the size of the resource size is fixed, you should use a bounded semaphore. Before spawning any worker threads, your main thread would initialize the semaphore:

    maxconnections = 5
    ...
    pool_sema = BoundedSemaphore(value=maxconnections)

Once spawned, worker threads call the semaphore's acquire and release methods when they need to connect to the server:

    pool_sema.acquire()
    conn = connectdb()
    ... use connection ...
    conn.close()
    pool_sema.release()

The use of a bounded semaphore reduces the chance that a programming error which causes the semaphore to be released more than it's acquired will go undetected.