C++ 当wait_for()状态为ready且wait()返回时,future get()会阻塞

C++ 当wait_for()状态为ready且wait()返回时,future get()会阻塞,c++,concurrency,std-future,C++,Concurrency,Std Future,我希望下面的代码每次都能通过所有断言并成功完成。目前,似乎每次都会在两个分支中阻塞std::future.get()。尽管wait_for()将状态显示为ready和wait()立即返回,但它仍会永久阻止。gcc 7.4.0和clang 6.0.0的结果相同 #include <chrono> #include <condition_variable> #include <future> #include <functional> #include

我希望下面的代码每次都能通过所有断言并成功完成。目前,似乎每次都会在两个分支中阻塞
std::future.get()。尽管
wait_for()
将状态显示为
ready
wait()
立即返回,但它仍会永久阻止。gcc 7.4.0和clang 6.0.0的结果相同

#include <chrono>
#include <condition_variable>
#include <future>
#include <functional>
#include <iostream>
#include <mutex>
#include <queue>

#include <cassert>
#include <unistd.h>


template<class T>
class BlockingQueue {
  std::queue<T> theQueue;
  std::mutex mtx;
  std::condition_variable hasDataCondition;

public:
  void push(const T& t) {
    std::unique_lock<std::mutex> lock{mtx};
    theQueue.push(t);
    hasDataCondition.notify_all();
  }

  T popWhenAvailable(int i = 0) {
    std::unique_lock<std::mutex> lock{mtx};
    if (theQueue.empty()) {
      std::cout << "Waiting " << i << std::endl;
      hasDataCondition.wait(lock, [this]{return not theQueue.empty();});
      std::cout << "Done waiting " << i << std::endl;
    }
    T front = std::move(theQueue.front());
    theQueue.pop();
    std::cout << "Got value " << front << " and popped it on " << i << std::endl;
    return front;
  }
};

int main(int argc, char** argv) {
  BlockingQueue<int> q;

  auto futureInt0 = std::async(std::launch::async, [&]{return q.popWhenAvailable();});
  auto futureInt1 = std::async(std::launch::async, [&]{return q.popWhenAvailable(1);});
  std::cout << "Starting threads..." << std::endl;
  sleep(2);

  assert(futureInt0.wait_for(std::chrono::milliseconds(300)) != std::future_status::ready);
  assert(futureInt1.wait_for(std::chrono::milliseconds(300)) != std::future_status::ready);

  std::cout << "Pushing data..." << std::endl;
  q.push(4);
  std::cout << "Pushed! Checking results..." << std::endl;

  if (futureInt0.wait_for(std::chrono::milliseconds(300)) == std::future_status::ready) {
    std::cout << "Future 0 ready." << std::endl;
    assert(futureInt1.wait_for(std::chrono::milliseconds(300)) != std::future_status::ready);
    std::cout << "Future 1 isn't ready (it shouldn't be)." << std::endl;

    std::cout << "Trying to wait() for future 0, should return immediately..." << std::endl;
    futureInt0.wait();
    std::cout << "Now get() the value..." << std::endl;
    assert(futureInt0.get() == 4);
  } else {
    std::cout << "Future 0 not ready. Trying future 1..." << std::endl;
    assert(futureInt1.wait_for(std::chrono::milliseconds(300)) == std::future_status::ready);

    std::cout << "Future1 status is ready. Trying to wait(), should return immediately..." << std::endl;
    futureInt1.wait();
    std::cout << "Now get() the value..." << std::endl;
    assert(futureInt1.get() == 4);
  }
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
模板
类阻塞队列{
std::队列队列;
std::互斥mtx;
std::condition_变量hasDataCondition;
公众:
无效推力(常数T&T){
std::unique_lock lock{mtx};
push(t);
hasDataCondition.notify_all();
}
T popWhenAvailable(int i=0){
std::unique_lock lock{mtx};
if(theQueue.empty()){

std::cout很有趣!我发现的第一件事是,您正在等待第二个线程,以获得@rafix07所指出的要弹出的内容。我不确定最终目标是什么,但这是有效的。我在MSVC上进行了测试,结果如下所示

#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
模板
类阻塞队列{
std::队列队列;
std::互斥mtx;
std::condition_变量hasDataCondition;
公众:
无效推力(常数T&T){
std::unique_lock lock{mtx};
push(t);
hasDataCondition.notify_all();
}
T popWhenAvailable(内部i){
std::unique_lock lock{mtx};

std::难道我看不出任何问题。
push
只被调用一次。您有两个
future
,只有一个可以完成,所以
main
挂起在第二个future的析构函数上…-它正在等待共享状态就绪,但这不会发生,因为推送到队列的数据不足。啊,好的。这就是原因。我没有想到
.get()
不是挂起的,而是另一个未来的dtor。
#include <chrono>
#include <condition_variable>
#include <future>
#include <functional>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
#include <cassert>


template<class T>
class BlockingQueue {
    std::queue<T> theQueue;
    std::mutex mtx;
    std::condition_variable hasDataCondition;

public:
    void push(const T& t) {
        std::unique_lock<std::mutex> lock{ mtx };
        theQueue.push(t);
        hasDataCondition.notify_all();
    }

    T popWhenAvailable(int i) {
        std::unique_lock<std::mutex> lock{ mtx };
        std::cout << "popWhenAvailable: " << i << std::endl;
        if (theQueue.empty()) {
            std::cout << "Waiting " << i << std::endl;
            hasDataCondition.wait(lock, [this] {return ! theQueue.empty(); });
            std::cout << "Done waiting " << i << std::endl;
        }
        T front = std::move(theQueue.front());
        theQueue.pop();
        std::cout << "Got value " << front << " and popped it on " << i << std::endl;
        return front;
    }
};

int main(int argc, char** argv) {
    using namespace std::chrono_literals;
    BlockingQueue<int> q;

    auto futureInt0 = std::async(std::launch::async, [&] {return q.popWhenAvailable(0); });
    auto futureInt1 = std::async(std::launch::async, [&] {return q.popWhenAvailable(1); });
    std::cout << "Starting threads...\n" << std::endl;
    std::this_thread::sleep_for(1000ms);

    assert(futureInt0.wait_for(std::chrono::milliseconds(300)) != std::future_status::ready);
    assert(futureInt1.wait_for(std::chrono::milliseconds(300)) != std::future_status::ready);

    std::cout << "Pushing data..." << std::endl;
    q.push(4);
    std::cout << "Pushed! Checking results..." << std::endl;

    std::pair<bool, bool> done = { false,false };
    for (;;) {
        if (!done.first && futureInt0.wait_for(std::chrono::milliseconds(300)) == std::future_status::ready) {
            std::cout << "Future 0 ready." << std::endl;
            futureInt0.wait();
            std::cout << "Now get() the value 0: " << futureInt0.get() << std::endl;
            done.first = true;
        }
        else if(!done.second && futureInt1.wait_for(std::chrono::milliseconds(300)) == std::future_status::ready) {
            std::cout << "Future 1 ready." << std::endl;
            futureInt1.wait();
            std::cout << "Now get() the value 1: " << futureInt1.get() << std::endl;
            done.second = true;
        }
        if (done.first && done.second)
            break;
        else if(done.first || done.second)
            q.push(8);
    }
}