简体   繁体   English

跨多个线程的unique_lock

[英]unique_lock across various threads

I have a consumer and two producers. 我有一个消费者和两个生产者。 When i spawn both producers at the same time, they seem to lock each other out as the first values we see are 223 and 889 from each. 当我同时产生两个生产者时,它们似乎互相锁定,因为我们看到的第一个值分别是223和889。

could somebody please explain what is happening here? 有人可以解释一下这里发生了什么吗?

#include<vector>
#include<thread>
#include<iostream>
#include<mutex>
#include<chrono>
#include <condition_variable>

using namespace std;

vector<double>testvec;
mutex mtx;
condition_variable cv;

class Base
{
public:
    Base() {};
    void dosomething();
    int i;
};

void Base::dosomething()
{
    while(1)
    {
        std::unique_lock<std::mutex> ulck(mtx);
        testvec.push_back(i);
        ulck.unlock();
        cv.notify_all();
        i++;
        std::this_thread::sleep_for (std::chrono::milliseconds(i));
    }
};

class Derived1 : public Base
{
public:
    Derived1() {i = 222;}
};

class Derived2 : public Base
{
public:
    Derived2() {i = 888;}
};

class Consumer
{
public:
    Consumer() {}
    void dostuff();
};

void Consumer::dostuff()
{
    while(1)
    {
        std::unique_lock<std::mutex> ulck(mtx);    //locks shared data
        cv.wait(ulck);
        cout<<"last value: "<<testvec.back()<<endl;
        ulck.unlock();
    }
}

int main( int argc, char ** argv )
{
    Derived1 derived1;
    Derived2 derived2;
    Consumer c;

    std::thread t1(&Derived1::dosomething, &derived1);
    std::thread t2(&Derived2::dosomething, &derived2);
    std::thread t3(&Consumer::dostuff, &c);

    t1.join();
    t2.join();
    t3.join();
}

output is:
last value: 223
last value: 224
last value: 225
last value: 889
last value: 226
last value: 227
last value: 228
last value: 229
last value: 890
last value: 230

expected output:
last value: 888 (or 222)
last value: 222 (or 888)
last value: 223
...

You will never get sane behavior from wait without a predicate. 没有谓词,您永远不会从wait获得理智的行为。 Also, why do you keep releasing the lock only to immediately lock it again? 另外,为什么只继续释放锁以立即再次锁定?

This is what you ened to do: 这是您要做的事情:

void Consumer::dostuff()
{
    std::unique_lock<std::mutex> ulck(mtx);    //locks shared data
    int i = 0;
    while(1)
    {

        // Correctly decide when to wait and when to stop waiting
        while (testvec.empty() || (testvec.back() == i))
            cv.wait(ulck);

        i = testvec.back();
        cout<<"last value: "<<testvec.back()<<endl;
    }
}

Condition variables are stateless and have no way to know whether they should wait or when they should stop waiting as those are functions of the shared state. 条件变量是无状态的,没有办法知道它们应该等待还是何时停止等待,因为它们是共享状态的函数。 It's your responsibility to code that, using the mutex to protect the shared state. 使用互斥锁保护共享状态是您的责任。

Notice this code only calls wait if it has to wait. 请注意,此代码仅在必须wait时才调用wait。 You can only tell if you need to wait by inspecting the shared state. 您只能通过检查共享状态来判断是否需要等待。 And notice it continue to call wait while it has to wait. 并注意它在必须wait同时继续调用wait。 Again, you can only tell if you can stop waiting by inspecting the shared state. 同样,您只能通过检查共享状态来判断是否可以停止等待。 The shared state is your responsibility -- condition variables are stateless. 共享状态是您的责任-条件变量是无状态的。

I have a consumer and two producers. 我有一个消费者和两个生产者。

Wrong. 错误。 You have 2 producers and a sampler. 您有2个生产者和一个采样器。 Nothing is consuming. 什么都没有消耗。

What you probably want is this (after simplification): 您可能想要的是(简化后):

#include<deque>
#include<thread>
#include<iostream>
#include<mutex>
#include<chrono>
#include <condition_variable>

using namespace std;

deque<double> testvec;
mutex mtx;
condition_variable cv;

auto producer = [](int i)
{
    while(1)
    {
        std::unique_lock<std::mutex> ulck(mtx);
        testvec.push_back(i);
        ulck.unlock();
        cv.notify_all();
        i++;
        std::this_thread::sleep_for (std::chrono::milliseconds(i));
    }
};

void consume()
{
    while(1)
    {
        std::unique_lock<std::mutex> ulck(mtx);    //locks shared data


        // wait until there is something to consume...
        cv.wait(ulck, [&]{return not testvec.empty(); });

        /// ... consume it...
        auto val = testvec.front();
        testvec.pop_front();

        /// ... now unlock and work with the consumed data while unlocked
        ulck.unlock();

        /// ... do work
        cout<<"last value: " << val <<endl;
    }
}

int main( int argc, char ** argv )
{
    std::thread t1(producer, 222);
    std::thread t2(producer, 888);
    std::thread t3(consume);

    t1.join();
    t2.join();
    t3.join();
}

example output: 示例输出:

last value: 222
last value: 888
last value: 223
last value: 224
last value: 225
last value: 889
last value: 226
...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM