我想在两个线程中计算两件事,其中每个线程都有无限循环。这两者并不相互依赖。
此外,我希望终端上的一些输出也是无限循环(每次计算最后一次输出时再次启动,而不是需要之间的任何步骤)。因此,我想要一些全局变量的局部副本(第一次计算的两次连续迭代和第二次计算的一次值)都处于安全状态。
现在的问题是我必须为这两个计算添加一些睡眠才能从func1获得一些输出。我检查了CPU的使用情况,睡眠确实降低了它。 我该如何解决这个问题?
另见测试评论:
#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
int n(0);
long s(1234);
double t(56.78);
std::condition_variable cond;
std::mutex m1;
std::mutex m2;
void func1() {
while (true) {
// Not needed due to wait unlocking the mutex
//std::this_thread::sleep_for(std::chrono::nanoseconds(1));
std::unique_lock<std::mutex> lck1(m1);
cond.wait(lck1);
int n1(n);
long s1(s);
cond.wait(lck1);
int n2(n);
long s2(s);
lck1.unlock();
std::unique_lock<std::mutex> lck2(m2);
double ti(t);
lck2.unlock();
// calculate and print some output
std::this_thread::sleep_for(std::chrono::milliseconds((n1*n2)/2));
std::cout << n1 << ":" << s1 << ", " << n2 << ":" << s2 << ", " << ti << std::endl;
}
}
void func2() {
while (true) {
// Why do I need this to make func1 ever proceed (ok, really seldom and random func1 got the mutex) and
// how to work around this without sleep lowering time for computations?
std::this_thread::sleep_for(std::chrono::nanoseconds(1)); // comment out to test
std::unique_lock<std::mutex> lck1(m1);
n++;
// do some stuff taking some time with s
std::this_thread::sleep_for(std::chrono::milliseconds((n*n)/3));
cond.notify_all();
}
}
void func3() {
while (true) {
// Why do I need this to make func1 ever proceed (it got the mutex never ever) and
// how to work around this without sleep lowering time for computations?
std::this_thread::sleep_for(std::chrono::nanoseconds(1)); // comment out to test
std::unique_lock<std::mutex> lck2(m2);
// do something taking some time with t
std::this_thread::sleep_for(std::chrono::seconds(2));
}
}
int main() {
std::thread t1(func1);
std::thread t2(func2);
std::thread t3(func3);
t1.join();
t2.join();
t3.join();
return 0;
}
答案 0 :(得分:1)
首先,您必须假设条件变量wait
调用实际上并不等待。一个有效的(虽然是极端的)实现是lk.unlock(); lk.lock();
,有时它的行为就像是由于&#34;虚假的唤醒&#34;。您确实需要将条件与条件变量相关联,如果不满足条件则循环。
其次,如果在保持互斥锁的同时通知条件变量,则等待线程将从通知中唤醒,仅阻塞互斥锁,因此通知线程可能会在等待线程获取之前重新获取互斥锁一个机会。
因此,在通知条件变量之前解锁互斥锁会更好。
lck1.unlock();
cond.notify_all();
这使得等待线程有机会在通知线程重新获取锁之前立即获取互斥锁。
您可以在迭代之间添加std::this_thread::yield()
次调用,以便其他线程有机会获取锁定。但是,这不是理想的 - 任何你必须明确做一些事情来解决调度程序的代码都是重写的目标。
答案 1 :(得分:0)
您可以用以下内容替换睡眠:
std::this_thread::yield();
这将使另一个线程有机会获得锁定。
答案 2 :(得分:0)
您可以在没有条件变量的情况下执行此操作:
long s[2] = {1234,1234}; // instead of a single value
void func1() {
while (true) {
std::unique_lock<std::mutex> lck1(m1);
int n1(n);
long s1(s[0]);
long s2(s[1]);
lck1.unlock();
int n2 = n1 + 1;
// ...rest of func1 is unchanged...
void func2() {
while (true) {
std::unique_lock<std::mutex> lck1(m1);
n++;
// compute s[0]
std::this_thread::sleep_for(std::chrono::milliseconds((n*n)/3));
n++;
// compute s[1]
std::this_thread::sleep_for(std::chrono::milliseconds((n*n)/3));
std::this_thread::yield();
}
}