使用循环使相同的线程不能一次又一次地获得相同的互斥锁而不会睡眠

时间:2017-03-18 20:16:16

标签: c++ multithreading c++11 mutex condition-variable

我想在两个线程中计算两件事,其中每个线程都有无限循环。这两者并不相互依赖。

此外,我希望终端上的一些输出也是无限循环(每次计算最后一次输出时再次启动,而不是需要之间的任何步骤)。因此,我想要一些全局变量的局部副本(第一次计算的两次连续迭代和第二次计算的一次值)都处于安全状态。

现在的问题是我必须为这两个计算添加一些睡眠才能从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;
}

3 个答案:

答案 0 :(得分:1)

首先,您必须假设条件变量wait调用实际上并不等待。一个有效的(虽然是极端的)实现是lk.unlock(); lk.lock();,有时它的行为就像是由于&#34;虚假的唤醒&#34;。您确实需要将条件与条件变量相关联,如果不满足条件则循环。

其次,如果在保持互斥锁的同时通知条件变量,则等待线程将从通知中唤醒,仅阻塞互斥锁,因此通知线程可能会在等待线程获取之前重新获取互斥锁一个机会。

因此,在通知条件变量之前解锁互斥锁会更好。

lck1.unlock();
cond.notify_all();

这使得等待线程有机会在通知线程重新获取锁之前立即获取互斥锁。

您可以在迭代之间添加std::this_thread::yield()次调用,以便其他线程有机会获取锁定。但是,这不是理想的 - 任何你必须明确做一些事情来解决调度程序的代码都是重写的目标。

答案 1 :(得分:0)

您可以用以下内容替换睡眠:

std::this_thread::yield();

这将使另一个线程有机会获得锁定。

http://en.cppreference.com/w/cpp/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();
    }
}