为什么没有std::future::try_wait()

Why there is no std::future::try_wait()?

本文关键字:wait try future std 为什么      更新时间:2023-10-16

鉴于有std::future::wait_for/until(),我不明白为什么没有std::future::try_wait()。我目前正在编写一个生产者-消费者示例,我想使用 std::future 作为向消费者线程发出返回信号的便捷方式。我的消费者代码就像

void consume(std::future<void>& stop) {
  while (!stop.try_wait()) { // alas, no such method
    // try consuming an item in queue
  }
}

我想用零持续时间wait_for()来模拟try_wait(),这真的很丑陋。作为一个附带问题:还有其他方便的方法来指示消费者线程返回吗?

>std::experimental::future添加了.is_ready().then( F )方法。

is_ready可能是您的try_wait(没有超时(。

如前所述,wait_for为您提供了try_wait在实践中的功能。


std::future不是设计为信号机制,即使它可以用作信号机制。 如果您想要一个信令机制,请使用条件变量、互斥锁和存储信号状态的状态(可能将它们组合在一起(创建一个。

struct state {
  bool stop = false;
  unsigned some_value = 7;
  friend auto as_tie( state const& s ) {
    return std::tie(s.stop, s.some_value);
  }
  friend bool operator==( state const& lhs, state const& rhs ) {
    return as_tie(lhs)==as_tie(rhs);
  }
};
template<class State, class Cmp=std::equal<State>>
struct condition_state {
  // gets a copy of the current state:
  State get_state() const {
    auto l = lock();
    return state;
  }
  // Returns a state that is different than in:
  State next_state(State const& in) const {
    auto l = lock();
    cv.wait( l, [&]{ return !Cmp{}(in, state); } );
    return state;
  }
  // runs f on the state if it changes from old.
  // does this atomically in a mutex, so be careful.
  template<class F>
  auto consume_state( F&& f, State old ) const {
    auto l = lock();
    cv.wait( l, [&]{ return !Cmp{}(old, state); } );
    return std::forward<F>(f)( state );
  }
  // runs f on the state if it changes:
  template<class F>
  auto consume_state( F&& f ) const {
    return consume_state( std::forward<F>(f), state );
  }
  // calls f on the state, then notifies everyone to check if
  // it has changed:
  template<class F>
  void change_state( F&& f ) {
    {
      auto l = lock();
      std::forward<F>(f)( state );
    }
    cv.notify_all();
  }
  // Sets the value of state to in
  void set_state( State in ) {
    change_state( [&](State& state) {
      state = std::move(in);
    } );
  }
private:
  auto lock() const { return std::unique_lock<std::mutex>(m); }
  mutable std::mutex m;
  std::condition_variable cv;
  State state;
};

例如,假设我们的State是就绪任务的向量和一个表示"中止"的布尔值:

struct tasks_todo {
  std::deque< std::function<void()> > todo;
  bool abort = false;
  friend bool operator==()( tasks_todo const& lhs, tasks_todo const& rhs ) {
    if (lhs.abort != rhs.abort) return false;
    if (lhs.todo.size() != rhs.todo.size()) return false;
    return true;
  }
};

然后我们可以按如下方式编写我们的队列:

struct task_queue {
  void add_task( std::function<void()> task ) {
    tasks.change_state( [&](auto& tasks) { tasks.todo.push_back(std::move(task)); } );
  }
  void shutdown() {
    tasks.change_state( [&](auto& tasks) { tasks.abort = true; } );
  }
  std::function<void()> pop_task() {
    return tasks.consume_state(
      [&](auto& tasks)->std::function<void()> {
        if (tasks.abort) return {};
        if (tasks.todo.empty()) return {}; // should be impossible
        auto r = tasks.front();
        tasks.pop_front();
        return r;
      },
      {} // non-aborted empty queue
    );
  }
private:
  condition_state<task_todo> tasks;
};

或诸如此类。

由于 std::future::wait_for 不可用,因此可以指定自己的超时例程,如代码片段所示:

void even(int n,promise<bool> p)
{
   this_thread::sleep_for(chrono::milliseconds(500ms)); //set milliseconds(10ms) to display result
   p.set_value( n%2 == 0?true:false);
}

int main()
{
    promise<bool> p;
    future<bool> f =p.get_future();
    int n = 100;
    std::chrono::system_clock::time_point tp1 = std::chrono::system_clock::now() ;
    thread t([&](){ even(n,move(p)); });      
    auto span = std::chrono::milliseconds(200ms);
    std::future_status s;
    do
    {
        s =f.wait_for(std::chrono::seconds(0));
        // do something
    }
    while(  std::chrono::system_clock::now() < (tp1 + span) );

    if( s==future_status::ready)
        std::cout << "result is " << (f.get()? "Even": "Odd")  << 'n';
    else
        std::cout << "timeout " << 'n';
    t.join();         
}