使用std :: conditional_variable等待条件 [英] Using std::conditional_variable to wait on a condition

查看:530
本文介绍了使用std :: conditional_variable等待条件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为简单起见,我们假设只有一个条件变量与布尔值反映的单个条件匹配.

1)为什么std::condition_variable::wait(...)在发送通知"以使其休眠后又再次锁定互斥锁?

2)看到"1)"中的行为,这意味着当您执行std::condition_variable::notify_all时,它只会使所有等待线程都被解除阻塞/唤醒……但是按顺序,而不是一次全部?如果是这样,该怎么办才能一次完成所有操作?

3)如果我只关心线程在满足条件之前就处于休眠状态,而不关心任何互斥量获取,那么我该怎么办?有没有其他选择,或者应该采用这种方法来破解当前的std::condition_variable::wait(...)方法?

如果要使用"hackery",此功能是否可以在条件下解除对所有个正在等待的线程的阻塞,并且可以从任何(每个线程)线程中调用它:

//declared somehwere and modified before sending "notify"(ies)
std::atomic<bool> global_shared_condition_atomic_bool;

//the single(for simplicity in our case) condition variable matched with the above boolean result
std::condition_variable global_shared_condition_variable;

static void MyClass:wait()
{
    std::mutex mutex;
    std::unique_lock<std::mutex> lock(mutex);

    while (!global_shared_condition_atomic_bool) global_shared_condition_variable.wait(lock);
}

它可能是从随机的等待"线程中调用的,就像这样:

void random_thread_run()
{
    while(someLoopControlValue)
    {
        //random code...
        MyClass:wait(); //wait for whatever condition the class+method is for.
        //more random code...
    }
}


门课程

#ifndef Gate_Header
#define Gate_Header

#include <mutex>
#include <condition_variable>

class Gate
{
public:
    Gate()
    {
        gate_open = false;
    }

    void open()
    {
        m.lock();
        gate_open = true;
        m.unlock();

        cv.notify_all();
    }

    void wait()
    {
        std::unique_lock<std::mutex> lock(m);

        while (!gate_open) cv.wait(lock);
    }

    void close()
    {
        m.lock();
        gate_open = false;
        m.unlock();
    }

private:
    std::mutex m;
    std::condition_variable cv;
    bool gate_open;
};

#endif

解决方案

条件变量会虚假地唤醒一切.

必须拥有一个互斥锁,并且必须必须保护某种消息才能使它们正常工作,或者您不能保证有任何此类唤醒发生.

之所以这样做是因为,无论如何,最终都无法以这样的虚假版本实现非虚假版本的有效实现.

如果您无法通过互斥来保护消息编辑(即,没有同步),则消息的状态是未定义的行为.这可能会导致编译器优化对内存的读取,从而在第一次读取后跳过它. /p>

即使不包括未定义的行为(想象您使用原子),在某些竞争条件下也会设置一条消息,发生一条通知,并且如果您未能在数据库中获取互斥体,则等待通知的任何人都不会看到该消息已被设置.设置变量和通知条件变量之间的时间.

除非有极端情况,否则您通常要使用wait的lambda版本.

除非审核通知代码和等待代码,否则无法审计条件变量代码.

struct gate {
  bool gate_open = false;
  mutable std::condition_variable cv;
  mutable std::mutex m;

  void open_gate() {
    std::unique_lock<std::mutex> lock(m);
    gate_open=true;
    cv.notify_all();
  }
  void wait_at_gate() const {
    std::unique_lock<std::mutex> lock(m);
    cv.wait( lock, [this]{ return gate_open; } );
  }
};

  void open_gate() {
    {
      std::unique_lock<std::mutex> lock(m);
      gate_open=true;
    }
    cv.notify_all();
  }

For simplicity, let's assume that we have only one conditional variable to match a single condition that is reflected by a boolean.

1) Why does std::condition_variable::wait(...) locks the mutex again after a "notify" has been sent to un-sleep it?

2) Seeing the behaviour in "1)", does that mean that when you do std::condition_variable::notify_all it only makes it so that all of the waiting threads are unblocked/woken up... but in order instead of all at once? If so, what can be done to do it all at once?

3) If I only care about threads sleeping until a condition is met and not care a single bit for any mutex acquisition, what can I do? Is there an alternative or should current std::condition_variable::wait(...) approach(es) be hacked around this?

If "hackery" is to be used, will this function work for unblocking all waiting threads on a condition and can it be called from any(per thread) threads:

//declared somehwere and modified before sending "notify"(ies)
std::atomic<bool> global_shared_condition_atomic_bool;

//the single(for simplicity in our case) condition variable matched with the above boolean result
std::condition_variable global_shared_condition_variable;

static void MyClass:wait()
{
    std::mutex mutex;
    std::unique_lock<std::mutex> lock(mutex);

    while (!global_shared_condition_atomic_bool) global_shared_condition_variable.wait(lock);
}

it would have been called from random "waiting" threads like so:

void random_thread_run()
{
    while(someLoopControlValue)
    {
        //random code...
        MyClass:wait(); //wait for whatever condition the class+method is for.
        //more random code...
    }
}


Edit:

Gate class

#ifndef Gate_Header
#define Gate_Header

#include <mutex>
#include <condition_variable>

class Gate
{
public:
    Gate()
    {
        gate_open = false;
    }

    void open()
    {
        m.lock();
        gate_open = true;
        m.unlock();

        cv.notify_all();
    }

    void wait()
    {
        std::unique_lock<std::mutex> lock(m);

        while (!gate_open) cv.wait(lock);
    }

    void close()
    {
        m.lock();
        gate_open = false;
        m.unlock();
    }

private:
    std::mutex m;
    std::condition_variable cv;
    bool gate_open;
};

#endif

解决方案

Condition variables wake things up spuriously.

You must have a mutex and it must guard a message of some kind for them to work, or you have zero guarantee that any such wakeup occurred.

This was done, presumably, because efficient implementations of a non-spurious version end up being implemeneted in terms of such a spurious version anyhow.

If you fail to guard the message editing with a mutex (ie, no synchronization on it, the state of the message is undefined behavior. This can cause compilers to optimize the read from memory to skip it after the first read.

Even excluding that undefined behavior (imagine you use atomics), there are race conditions where a message is set, a notification occurs, and nobody waiting on the notification sees the message being set if you fail to have the mutex acquired in the time between the variable being set and the condition variable being notified.

Barring extreme cases, you usually want to use the lambda version of wait.

Auditing condition variable code is not possible unless you audit both the notification code and the wait code.

struct gate {
  bool gate_open = false;
  mutable std::condition_variable cv;
  mutable std::mutex m;

  void open_gate() {
    std::unique_lock<std::mutex> lock(m);
    gate_open=true;
    cv.notify_all();
  }
  void wait_at_gate() const {
    std::unique_lock<std::mutex> lock(m);
    cv.wait( lock, [this]{ return gate_open; } );
  }
};

or

  void open_gate() {
    {
      std::unique_lock<std::mutex> lock(m);
      gate_open=true;
    }
    cv.notify_all();
  }

这篇关于使用std :: conditional_variable等待条件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆