跳转至

1115. 交替打印 FooBar

题目描述

给你一个类:

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 实例:

  • 线程 A 将会调用 foo() 方法,而
  • 线程 B 将会调用 bar() 方法

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

 

示例 1:

输入:n = 1
输出:"foobar"
解释:这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,"foobar" 将被输出一次。

示例 2:

输入:n = 2
输出:"foobarfoobar"
解释:"foobar" 将被输出两次。

 

提示:

  • 1 <= n <= 1000

解法

方法一:多线程 + 信号量

我们用两个信号量 \(f\)\(b\) 来控制两个线程的执行顺序,其中 \(f\) 初始值为 \(1\),而 \(b\) 初始值为 \(0\),表示线程 \(A\) 先执行。

当线程 \(A\) 执行时,首先会执行 \(f\)\(acquire\) 操作,此时 \(f\) 的值变为 \(0\),线程 \(A\) 获得了 \(f\) 的使用权,可以执行 \(foo\) 函数,然后执行 \(b\)\(release\) 操作,此时 \(b\) 的值变为 \(1\),线程 \(B\) 获得了 \(b\) 的使用权,可以执行 \(bar\) 函数。

当线程 \(B\) 执行时,首先会执行 \(b\)\(acquire\) 操作,此时 \(b\) 的值变为 \(0\),线程 \(B\) 获得了 \(b\) 的使用权,可以执行 \(bar\) 函数,然后执行 \(f\)\(release\) 操作,此时 \(f\) 的值变为 \(1\),线程 \(A\) 获得了 \(f\) 的使用权,可以执行 \(foo\) 函数。

因此,我们只需要循环 \(n\) 次,每次执行 \(foo\)\(bar\) 函数时,先执行 \(acquire\) 操作,再执行 \(release\) 操作即可。

时间复杂度 \(O(n)\),空间复杂度 \(O(1)\)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from threading import Semaphore


class FooBar:
    def __init__(self, n):
        self.n = n
        self.f = Semaphore(1)
        self.b = Semaphore(0)

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

    def bar(self, printBar: "Callable[[], None]") -> None:
        for _ in range(self.n):
            self.b.acquire()
            # printBar() outputs "bar". Do not change or remove this line.
            printBar()
            self.f.release()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class FooBar {
    private int n;
    private Semaphore f = new Semaphore(1);
    private Semaphore b = new Semaphore(0);

    public FooBar(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            f.acquire(1);
            // printFoo.run() outputs "foo". Do not change or remove this line.
            printFoo.run();
            b.release(1);
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            b.acquire(1);
            // printBar.run() outputs "bar". Do not change or remove this line.
            printBar.run();
            f.release(1);
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <semaphore.h>

class FooBar {
private:
    int n;
    sem_t f, b;

public:
    FooBar(int n) {
        this->n = n;
        sem_init(&f, 0, 1);
        sem_init(&b, 0, 0);
    }

    void foo(function<void()> printFoo) {
        for (int i = 0; i < n; i++) {
            sem_wait(&f);
            // printFoo() outputs "foo". Do not change or remove this line.
            printFoo();
            sem_post(&b);
        }
    }

    void bar(function<void()> printBar) {
        for (int i = 0; i < n; i++) {
            sem_wait(&b);
            // printBar() outputs "bar". Do not change or remove this line.
            printBar();
            sem_post(&f);
        }
    }
};

评论