简体   繁体   English

C ++ 11 - 无法使用std :: thread和std :: condition_variable唤醒线程

[英]C++11 - can't awake a thread using std::thread and std::condition_variable

I'm stuck on a problem when trying to awake a thread by another one. 当我试图用另一个线程唤醒一个线程时,我遇到了一个问题。 A simple producer / consumer thing. 简单的生产者/消费者的事物。

Below the code. 代码下方。 Line 85 is the point I don't understand why it's not working. 第85行是我不明白的原因,为什么它不起作用。 The producer thread fills up a std::queue and calls std::condition_variable.notify_one() while the consumer thread is waiting for NOT std::queue.empty(). 生产者线程填充std :: queue并在消费者线程等待NOT std :: queue.empty()时调用std :: condition_variable.notify_one()。

Thanks in advance for any help 在此先感谢您的帮助

#include <mutex>
#include <condition_variable>
#include <queue>
#include <string>
#include <iostream>
#include <thread>

// request
class request :
    public std::mutex,
      public std::condition_variable,
      public std::queue<std::string>
{
public:
  virtual ~request();
};

request::~request()
{
}

// producer
class producer
{
public:
  producer(request &);

  virtual ~producer();

  void operator()();

private:
  request & request_;
};

producer::producer(request & _request)
:
request_(_request)
{
}

producer::~producer()
{
}

void
producer::operator()()
{
  while (true) {
    std::lock_guard<std::mutex> lock(request_);
    std::cout << "producer\n";
    request_.push("something");
    std::this_thread::sleep_for(std::chrono::seconds(1));
    request_.notify_one();
  }
}

class consumer
{
public:
  consumer(request &);

  virtual ~consumer();

  void operator()();

private:
  request & request_;
};

consumer::consumer(request & _request)
:
request_(_request)
{
}

consumer::~consumer()
{
}

void
consumer::operator()()
{
  while (true) {
    std::unique_lock<std::mutex> lock(request_); // <-- the problem
    std::cout << "consumer\n";
    request_.wait (
      lock, [this] {return !request_.empty();}
    );
    request_.pop();
  }
}

int
main()
{
  // request
  request request_;

  // producer
  std::thread producer_{producer(request_)};

  // consumer
  std::thread first_consumer_{consumer(request_)};
  std::thread second_consumer_{consumer(request_)};

  // join
  producer_.join();
  first_consumer_.join();
  second_consumer_.join();
}

Fixed code below, with these changes: 修正了以下代码,包含以下更改:

  • Don't derive from the mutex, condvar and queue like that, it's horrible. 不要像那样从互斥体,condvar和队列派生,这太可怕了。
  • Unlock the mutex AS SOON AS POSSIBLE after adding an item to the queue, critical sections should always be as small as possible. 将项目添加到队列后解锁互斥尽快 ,关键部分应该总是尽可能小。 This allows the consumers to wake up while the producer is sleeping. 这允许消费者在制片人睡觉时醒来。
  • Flush cout (I used endl to do that) so the output is printed immediately, this makes it easier to see what's happening. Flush cout (我使用endl来做),所以输出立即打印,这样可以更容易地看到发生了什么。
  • Print "consumer" after waking, because that's when the consumer is consuming, otherwise you get misleading output showing when the consumer is sleeping, not when it gets work to do. 醒来之后打印"consumer" ,因为那是消费者消费的时候,否则你会得到误导性的输出,表明消费者在睡觉时,而不是在工作时间。

The main problem with your code was the producer never gave the consumers a chance to run. 你的代码的主要问题是生产者从未给消费者一个运行的机会。 It added to the queue, slept for a second (still holding the mutex lock) then notified the condition variable (still holding the mutex), then really quickly released the mutex lock and acquired it again. 它添加到队列中,睡了一秒钟(仍然保持互斥锁)然后通知条件变量(仍然持有互斥锁),然后真正快速释放互斥锁并再次获取它。 Probably what you saw is that a consumer thread got the notification, tried to acquire the mutex lock, found it was still locked (by the producer thread) and so went back to sleep. 可能你看到的是消费者线程得到通知,试图获取互斥锁,发现它仍然被锁定(由生产者线程),所以又回到了睡眠状态。 The producer never released the mutex long enough for another thread to acquire it. 生产者从来没有释放过互斥锁,足以让另一个线程获得它。 You might have been able to get better results by adding a std::this_thread::yield() at the start of the producer loop, before locking the mutex, but algorithms that rely on yield() for correctness are generally broken (and indeed it makes no difference in my tests); 通过锁定互斥锁之前在生成器循环的开头添加std::this_thread::yield() ,您可能能够获得更好的结果,但依赖于yield()以获得正确性的算法通常会被破坏(实际上它在我的测试中没有任何区别); it's better to fix the producer loop to give the consumers a chance to wake up and run. 最好修复生产者循环,让消费者有机会醒来跑步。

Here's the working code: 这是工作代码:

#include <mutex>
#include <condition_variable>
#include <queue>
#include <string>
#include <iostream>
#include <thread>

// request
struct request
{
    std::mutex mx;
    std::condition_variable cv;
    std::queue<std::string> q;
};

// producer
class producer
{
public:
  producer(request & r) : request_(r) { }

  void operator()();

private:
  request & request_;
};

void
producer::operator()()
{
    while (true) {
        {
            std::lock_guard<std::mutex> lock(request_.mx);
            std::cout << "producer" << std::endl;
            request_.q.push("something");
        }
        std::this_thread::sleep_for(std::chrono::seconds(1));
        request_.cv.notify_one();
    }
}

class consumer
{
public:
  consumer(request & r) : request_(r) { }

  void operator()();

private:
  request & request_;
};

void
consumer::operator()()
{
  while (true) {
    std::unique_lock<std::mutex> lock(request_.mx);
    request_.cv.wait (
      lock, [this] {return !request_.q.empty();}
    );
    std::cout << "consumer" << std::endl;
    request_.q.pop();
  }
}

int
main()
{
  // request
  request request_;

  // producer
  std::thread producer_{producer(request_)};

  // consumer
  std::thread first_consumer_{consumer(request_)};
  std::thread second_consumer_{consumer(request_)};

  // join
  producer_.join();
  first_consumer_.join();
  second_consumer_.join();
}

You have to unlock your std::unique_lock before calling notify_one() otherwise your while loop will try to lock twice in the same thread. 您必须在调用notify_one()之前解锁std::unique_lock ,否则您的while循环将尝试在同一个线程中锁定两次。 This is valid for both producer and consumer. 这对生产者和消费者都有效。

However, I agree with those that say that your derivation on request is very misleading. 但是,我同意那些说你的请求推导是非常误导的。 Your should be using composition. 你应该使用作文。 If you give me 10 mins, I might come up with something that works :) 如果你给我10分钟,我可能会想出一些有用的东西:)

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

相关问题 C ++ 11 std :: condition_variable:我们可以直接将锁传递给通知的线程吗? - C++11 std::condition_variable: can we pass our lock directly to the notified thread? 使用std :: atomic比较std :: condition_variable wrt在C ++中暂停和恢复std :: thread的方法 - Approach of using an std::atomic compared to std::condition_variable wrt pausing & resuming an std::thread in C++ 在 C++ 中使用 std::thread 和 std::condition_variable 自定义后台任务的 SIGABRT - SIGABRT on custom background task using std::thread and std::condition_variable in C++ 在c ++ 11中使用带有std :: thread的atomics - Using atomics with std::thread in c++11 std :: condition_variable线程争用 - std::condition_variable thread contention std :: condition_variable线程安全吗? - Is std::condition_variable thread-safe? C ++ 11:为什么std :: condition_variable使用std :: unique_lock? - C++11: why does std::condition_variable use std::unique_lock? C ++:如何在UI线程和工作程序std :: thread之间使用std :: condition_variable - C++ : How to use an std::condition_variable between UI thread & worker std::thread 使用C ++ 11 std :: condition_variable的Gtest意味着valgrind错误 - Gtest with C++11 std::condition_variable implies valgrind errors C ++ 11无法将std :: condition_variable :: wait从&#39;void&#39;转换为&#39;bool&#39; - C++11 could not convert std::condition_variable::wait from 'void' to 'bool'
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM