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

use*_*451 5 c++ multithreading condition-variable c++11

当我试图用另一个线程唤醒一个线程时,我遇到了一个问题.简单的生产者/消费者的事物.

代码下方.第85行是我不明白的原因,为什么它不起作用.生产者线程填充std :: queue并在消费者线程等待NOT std :: queue.empty()时调用std :: condition_variable.notify_one().

在此先感谢您的帮助

#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();
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*ely 12

修正了以下代码,包含以下更改:

  • 不要像那样从互斥体,condvar和队列派生,这太可怕了.
  • 解锁互斥尽快将项目添加到队列后,关键部分应该总是尽可能小.这允许消费者在制片人睡觉时醒来.
  • Flush cout(我endl以前这样做)所以输出立即打印,这样可以更容易地看到发生了什么.
  • 醒来"consumer" 打印,因为那是消费者消费的时候,否则你会得到误导性的输出,表明消费者正在睡觉时,而不是当它开始工作时.

你的代码的主要问题是生产者从未给消费者一个运行的机会.它添加到队列中,睡了一秒钟(仍然保持互斥锁)然后通知条件变量(仍然持有互斥锁),然后真正快速释放互斥锁并再次获取它.可能你看到的是消费者线程得到通知,试图获取互斥锁,发现它仍然被锁定(由生产者线程),所以又回到了睡眠状态.生产者从来没有释放过互斥锁,足以让另一个线程获得它.你可能通过锁定互斥锁之前std::this_thread::yield()在生产者循环的开头添加一个来获得更好的结果,但是依赖于正确性的算法通常会被破坏(事实上它在我的测试中没有区别); 最好修复生产者循环,让消费者有机会醒来跑步.yield()

这是工作代码:

#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();
}
Run Code Online (Sandbox Code Playgroud)


dav*_*sia 0

您必须std::unique_lock在调用之前解锁notify_one(),否则您的 while 循环将尝试在同一线程中锁定两次。这对于生产者和消费者都有效。

然而,我同意那些说你根据要求进行的推导非常具有误导性的观点。你应该使用组合。如果你给我 10 分钟,我可能会想出一些有用的东西:)

  • 在通知之前无需解锁。然而,需要在**某处**解锁,以便其他线程有机会获得互斥体。 (2认同)