1114. 按序打印
给你一个类:
public class Foo {
public void first() { print(“first”); }
public void second() { print(“second”); }
public void third() { print(“third”); }
}
三个不同的线程 A、B、C 将会共用一个 Foo 实例。
线程 A 将会调用 first() 方法
线程 B 将会调用 second() 方法
线程 C 将会调用 third() 方法
请设计修改程序,以确保 second() 方法在 first() 方法之后被执行,third() 方法在 second() 方法之后被执行。
提示:
尽管输入中的数字似乎暗示了顺序,但是我们并不保证线程在操作系统中的调度顺序。
你看到的输入格式主要是为了确保测试的全面性。
class Foo {
public:
mutex a,b;
Foo() {
a.lock();
b.lock();
}
void first(function<void()> printFirst) {
// printFirst() outputs "first". Do not change or remove this line.
printFirst();
a.unlock();
}
void second(function<void()> printSecond) {
a.lock();
// printSecond() outputs "second". Do not change or remove this line.
printSecond();
b.unlock();
}
void third(function<void()> printThird) {
b.lock();
// printThird() outputs "third". Do not change or remove this line.
printThird();
}
};
1115. 交替打印 FooBar
已解答
中等
相关标签
premium lock icon
相关企业
给你一个类:
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 次。
class FooBar {
private:
int n;
mutex m1,m2;
public:
FooBar(int n) {
this->n = n;
m2.lock();
}
void foo(function<void()> printFoo) {
for (int i = 0; i < n; i++) {
m1.lock();
// printFoo(。) outputs "foo". Do not change or remove this line.
printFoo();
m2.unlock();
}
}
void bar(function<void()> printBar) {
for (int i = 0; i < n; i++) {
m2.lock();
// printBar() outputs "bar". Do not change or remove this line.
printBar();
m1.unlock();
}
}
};
1116. 打印零与奇偶数
已解答
中等
相关标签
premium lock icon
相关企业
现有函数 printNumber 可以用一个整数参数调用,并输出该整数到控制台。
例如,调用 printNumber(7) 将会输出 7 到控制台。
给你类 ZeroEvenOdd 的一个实例,该类中有三个函数:zero、even 和 odd 。ZeroEvenOdd 的相同实例将会传递给三个不同线程:
线程 A:调用 zero() ,只输出 0
线程 B:调用 even() ,只输出偶数
线程 C:调用 odd() ,只输出奇数
修改给出的类,以输出序列 “010203040506…” ,其中序列的长度必须为 2n 。
实现 ZeroEvenOdd 类:
ZeroEvenOdd(int n) 用数字 n 初始化对象,表示需要输出的数。
void zero(printNumber) 调用 printNumber 以输出一个 0 。
void even(printNumber) 调用printNumber 以输出偶数。
void odd(printNumber) 调用 printNumber 以输出奇数。
class ZeroEvenOdd {
private:
int n;
atomic<int> p=1;
condition_variable cv;
mutex mtx;
public:
ZeroEvenOdd(int n) {
this->n = n;
}
// printNumber(x) outputs "x", where x is an integer.
void zero(function<void(int)> printNumber) {
unique_lock<mutex> lck(mtx);
for(int i=1;i<=n;i++) {
cv.wait(lck,[&]{return p%2==1;});
printNumber(0);
cout<<0<<endl;
++p;
cv.notify_all();
}
}
void even(function<void(int)> printNumber) {
unique_lock<mutex> lck(mtx);
for(int i=1;i<=n/2;i++) {
cv.wait(lck,[&]{return p%4==0;});
printNumber(i*2);
p++;
cv.notify_all();
}
}
void odd(function<void(int)> printNumber) {
unique_lock<mutex> lck(mtx);
for(int i=1;i<=(n+1)/2;i++) {
cv.wait(lck,[&]{return p%4==2;});
printNumber(i*2-1);
p++;
cv.notify_all();
}
}
};
1117. H2O 生成
已解答
中等
相关标签
premium lock icon
相关企业
现在有两种线程,氧 oxygen 和氢 hydrogen,你的目标是组织这两种线程来产生水分子。
存在一个屏障(barrier)使得每个线程必须等候直到一个完整水分子能够被产生出来。
氢和氧线程会被分别给予 releaseHydrogen 和 releaseOxygen 方法来允许它们突破屏障。
这些线程应该三三成组突破屏障并能立即组合产生一个水分子。
你必须保证产生一个水分子所需线程的结合必须发生在下一个水分子产生之前。
换句话说:
如果一个氧线程到达屏障时没有氢线程到达,它必须等候直到两个氢线程到达。
如果一个氢线程到达屏障时没有其它线程到达,它必须等候直到一个氧线程和另一个氢线程到达。
书写满足这些限制条件的氢、氧线程同步代码。
class H2O {
public:
mutex mtx;
condition_variable cv;
atomic<int> h=0,o=0,flagh=0,flago=0;
H2O() {
}
void hydrogen(function<void()> releaseHydrogen) {
unique_lock<mutex> lck(mtx);
h++;
cv.wait(lck,[&]{return flagh || !flagh&&!flago&&h>=2&&o>=1;});
if(!flagh && !flago) {
flagh=2,flago=1;
}
--h;--flagh;
// releaseHydrogen() outputs "H". Do not change or remove this line.
releaseHydrogen();
cv.notify_all();
}
void oxygen(function<void()> releaseOxygen) {
unique_lock<mutex> lck(mtx);
++o;
cv.wait(lck,[&]{return flago || !flagh&&!flago&&h>=2&&o>=1;});
if(!flagh && !flago) {
flagh=2,flago=1;
}
--o;--flago;
// releaseOxygen() outputs "O". Do not change or remove this line.
releaseOxygen();
cv.notify_all();
}
};
1195. 交替打印字符串
已解答
中等
相关标签
premium lock icon
相关企业
编写一个可以从 1 到 n 输出代表这个数字的字符串的程序,但是:
如果这个数字可以被 3 整除,输出 “fizz”。
如果这个数字可以被 5 整除,输出 “buzz”。
如果这个数字可以同时被 3 和 5 整除,输出 “fizzbuzz”。
例如,当 n = 15,输出: 1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz。
假设有这么一个类:
class FizzBuzz {
public FizzBuzz(int n) { … } // constructor
public void fizz(printFizz) { … } // only output “fizz”
public void buzz(printBuzz) { … } // only output “buzz”
public void fizzbuzz(printFizzBuzz) { … } // only output “fizzbuzz”
public void number(printNumber) { … } // only output the numbers
}
请你实现一个有四个线程的多线程版 FizzBuzz, 同一个 FizzBuzz 实例会被如下四个线程使用:
线程A将调用 fizz() 来判断是否能被 3 整除,如果可以,则输出 fizz。
线程B将调用 buzz() 来判断是否能被 5 整除,如果可以,则输出 buzz。
线程C将调用 fizzbuzz() 来判断是否同时能被 3 和 5 整除,如果可以,则输出 fizzbuzz。
线程D将调用 number() 来实现输出既不能被 3 整除也不能被 5 整除的数字。
class FizzBuzz {
private:
int n;
mutex mt;
condition_variable cv;
atomic<int> x=1;
public:
FizzBuzz(int n) {
this->n = n;
}
// printFizz() outputs "fizz".
void fizz(function<void()> printFizz) {
unique_lock<mutex> lc(mt);
while(x<=n) {
cv.wait(lc,[&]{return x>n || x%3==0 && x%5!=0;});
if(x>n) {
break;
}
printFizz();
++x;
cv.notify_all();
}
}
// printBuzz() outputs "buzz".
void buzz(function<void()> printBuzz) {
unique_lock<mutex> lc(mt);
while(x<=n) {
cv.wait(lc,[&]{return x>n || x%3!=0 && x%5==0;});
if(x>n) {
break;
}
printBuzz();
++x;
cv.notify_all();
}
}
// printFizzBuzz() outputs "fizzbuzz".
void fizzbuzz(function<void()> printFizzBuzz) {
unique_lock<mutex> lc(mt);
while(x<=n) {
cv.wait(lc,[&]{return x>n || x%3==0 && x%5==0;});
if(x>n) {
break;
}
printFizzBuzz();
++x;
cv.notify_all();
}
}
// printNumber(x) outputs "x", where x is an integer.
void number(function<void(int)> printNumber) {
unique_lock<mutex> lc(mt);
while(x<=n) {
cv.wait(lc,[&]{return x>n || x%3!=0 && x%5!=0;});
if(x>n) {
break;
}
printNumber(x);
++x;
cv.notify_all();
}
}
};
代码
测试用例
测试结果
测试结果
1226. 哲学家进餐
已解答
中等
相关标签
premium lock icon
相关企业
5 个沉默寡言的哲学家围坐在圆桌前,每人面前一盘意面。叉子放在哲学家之间的桌面上。(5 个哲学家,5 根叉子)
所有的哲学家都只会在思考和进餐两种行为间交替。哲学家只有同时拿到左边和右边的叉子才能吃到面,而同一根叉子在同一时间只能被一个哲学家使用。每个哲学家吃完面后都需要把叉子放回桌面以供其他哲学家吃面。只要条件允许,哲学家可以拿起左边或者右边的叉子,但在没有同时拿到左右叉子时不能进食。
假设面的数量没有限制,哲学家也能随便吃,不需要考虑吃不吃得下。
设计一个进餐规则(并行算法)使得每个哲学家都不会挨饿;也就是说,在没有人知道别人什么时候想吃东西或思考的情况下,每个哲学家都可以在吃饭和思考之间一直交替下去。
问题描述和图片来自维基百科 wikipedia.org
哲学家从 0 到 4 按 顺时针 编号。请实现函数 void wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork):
philosopher 哲学家的编号。
pickLeftFork 和 pickRightFork 表示拿起左边或右边的叉子。
eat 表示吃面。
putLeftFork 和 putRightFork 表示放下左边或右边的叉子。
由于哲学家不是在吃面就是在想着啥时候吃面,所以思考这个方法没有对应的回调。
给你 5 个线程,每个都代表一个哲学家,请你使用类的同一个对象来模拟这个过程。在最后一次调用结束之前,可能会为同一个哲学家多次调用该函数。
class DiningPhilosophers {
public:
mutex mtx;
deque<atomic<int > > v;
condition_variable cv;
DiningPhilosophers() {
for(int i=0;i<=4;i++) v.emplace_back(0);
}
void wantsToEat(int philosopher,
function<void()> pickLeftFork,
function<void()> pickRightFork,
function<void()> eat,
function<void()> putLeftFork,
function<void()> putRightFork) {
unique_lock<mutex> lck(mtx);
cv.wait(lck,[&]{return !v[philosopher]&&!v[(philosopher+1)%5]; });
pickLeftFork();
pickRightFork();
eat();
putLeftFork();
putRightFork();
cv.notify_all();
}
};