【发布时间】:2020-03-27 06:42:05
【问题描述】:
假设给你以下代码:
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 次。
对于leetcode下面的问题我们要写两个函数
void foo(function<void()> printFoo);
void bar(function<void()> printBar);
其中printFoo 和相应的printBar 是一个打印Foo 的函数指针。函数foo 和bar 在多线程环境中被调用,并且没有关于如何调用foo 和bar 的顺序保证。
我的解决方案是
class FooBar {
private:
int n;
mutex m1;
condition_variable cv;
condition_variable cv2;
bool flag;
public:
FooBar(int n) {
this->n = n;
flag=false;
}
void foo(function<void()> printFoo) {
for (int i = 0; i < n; i++) {
unique_lock<mutex> lck(m1);
cv.wait(lck,[&]{return !flag;});
printFoo();
flag=true;
lck.unlock();
cv2.notify_one();
}
}
void bar(function<void()> printBar) {
for (int i = 0; i < n; i++) {
unique_lock<mutex> lck(m1);
cv2.wait(lck,[&]{return flag;});
printBar();
flag=false;
lck.unlock();
cv.notify_one();
// printBar() outputs "bar". Do not change or remove this line.
}
}
};
让我们假设,在时间 t = 0 bar 被调用,然后在时间 t = 10 foo 被调用,foo 穿过由互斥锁 m1 保护的临界区。
我的问题是
由于防护属性,C++ 内存模型是否保证当bar 函数从等待cv2 恢复时,flag 的值将设置为 true?
我是否正确假设线程之间共享的锁强制执行之前和之后的关系,如 Leslie Lamports 时钟系统的方式所示。编译器和 C++ 保证临界区(这里是锁的结束)结束之前的所有内容都将被任何租用锁的线程观察到,因此可以将常见的锁、原子、信号量可视化为前后执行通过在多线程环境中建立时间来行为。
我们可以只使用一个条件变量来解决这个问题吗?
有没有办法在不使用锁而只使用原子的情况下做到这一点。原子对锁有哪些性能改进?
如果我在关键区域内执行cv.notify_one() 和相应的cv2.notify_one() 会发生什么情况,是否有可能错过中断。
原来的问题 https://leetcode.com/problems/print-foobar-alternately/。
Leslie Lamports 论文 https://lamport.azurewebsites.net/pubs/time-clocks.pdf
【问题讨论】:
标签: c++ multithreading