首先,要想写出本题,首先先要理解一个概念,就是Semaphore对象

Semaphore()

Semaphore对象内部管理着一个计数器,该计数器由每个acquire()调用递减,并由每个release()调用递增。计数器永远不会低于0,当acquire()发现计数器为0的时候,线程阻塞,等待其他线程调用release()`。

import time
import threading
 
def test():
    time.sleep(2)
    print(time.ctime())
 
for i in range(5):
    t1=threading.Thread(target=test,args=()) # 实例化一个线程
    t1.start()  # 启动线程

执行结果:

Thu Jul 18 14:48:01 2019
Thu Jul 18 14:48:01 2019
Thu Jul 18 14:48:01 2019
Thu Jul 18 14:48:01 2019
Thu Jul 18 14:48:01 2019

可以看到,程序在很短时间内生成了5个线程

如果是在主机执行IO密集型任务的时候再执行这种类型的程序时,主机有很大可能因为消耗资源过多而宕机。

这时候就可以为这段程序添加一个计数器功能,来限制一个时间点内的线程数量。

import time
import threading

s1=threading.Semaphore(5)   #添加一个计数器

def test():
    s1.acquire()    #计数器获得锁,计数器值-1
    time.sleep(2)   #程序休眠2秒
    print(time.ctime())
    s1.release()    #计数器释放锁,计数器值+1

for i in range(10):
    t1=threading.Thread(target=test,args=()) #创建线程
    t1.start()  #启动线程

执行结果:

Thu Jul 18 14:55:54 2019
Thu Jul 18 14:55:54 2019
Thu Jul 18 14:55:54 2019
Thu Jul 18 14:55:54 2019
Thu Jul 18 14:55:54 2019
Thu Jul 18 14:55:56 2019
Thu Jul 18 14:55:56 2019
Thu Jul 18 14:55:56 2019
Thu Jul 18 14:55:56 2019
Thu Jul 18 14:55:56 2019

可以看到,由于添加的信号量初始值设定为5,所以线程以每五个进行了“分隔”。

题解

题目描述

提供一个类:

class FooBar {
  public void foo() {
    for (int i = 0; i < n; i++) {
      print("foo");
    }
  }

  public void bar() {
    for (int i = 0; i < n; i++) {
      print("bar");
    }
  }
}

两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。

请设计修改程序,以确保 “foobar” 被输出 n 次。

解题思路

使用两个信号量计数器,来对foo函数和bar函数分别加锁。

在调用foo函数的时候,管理foo的信号量计数器foo_lock-1,管理bar的信号量计数器bar_lock+1。
反之,在调用bar函数的时候,管理foo的信号量计数器foo_lock+1,管理bar的信号量计数器bar_lock-1。

题目解答

import threading
class FooBar:
    def __init__(self, n):
        self.n = n
        self.foo_lock = threading.Semaphore() #添加信号量计数器,初始值为1
        self.foo_lock.acquire() # 令两个计数器的值为0,造成线程阻塞
        self.bar_lock = threading.Semaphore()
        self.bar_lock.acquire()

    def foo(self, printFoo: 'Callable[[], None]') -> None:
        for i in range(self.n):
            # printFoo() outputs "foo". Do not change or remove this line.
            printFoo()
            self.bar_lock.release()
            self.foo_lock.acquire()

    def bar(self, printBar: 'Callable[[], None]') -> None:
        for i in range(self.n):
            self.bar_lock.acquire()
            # printBar() outputs "bar". Do not change or remove this line.
            printBar()
            self.foo_lock.release()