std::condition_variable::wait_for 的两种重载用法

一种重载是

    template <class _Rep, class _Period, class _Predicate>
    bool wait_for(unique_lock<mutex>& _Lck, const chrono::duration<_Rep, _Period>& _Rel_time, _Predicate _Pred) {
        // wait for signal with timeout and check predicate
        return _Wait_until1(_Lck, _To_absolute_time(_Rel_time), _Pred);
    }

用法示例:

#include <mutex>
#include <iostream>
#include <windows.h>
std::condition_variable cv;
std::mutex mu;
using namespace std::chrono_literals;
bool flag = false;
bool noticed_flag = false;
int main() {
	
	auto predicate_func = [&]() {
		if (flag && noticed_flag) {
			flag = false;
			noticed_flag = false;
			return true;
		}
		else {
			return false;
		}
	};

	auto wait_func = [&](int wait_time_out) {
		std::unique_lock<std::mutex> lck(mu);
		bool status = cv.wait_for(lck, std::chrono::milliseconds(wait_time_out), predicate_func);
		std::cout << int(status) << std::endl;
	};

	auto wake_and_wait = [&](bool flag_,int wait_time_out,int wake_delay) {

		std::thread th(wait_func, wait_time_out);

        /*
        由于没有加延时,th 在执行wait_func之前,flag已经被外部设置了。所以基本上,大概率,
        wait_func 的 cv.wait_for 在执行之前 flag 已经是 true 了。向系统要线程,延迟个3~4毫秒
        都是可能的。
        */
		/*
		模拟在 wait_for 和 notify_one 之前就把数据(谓词)准备好。放到 std::thread th(wait_func, wait_time_out); 之前也可以,放在这里主要是展示线程启动是有延迟的。
		*/
		{
			std::lock_guard<std::mutex> lck(mu);
			flag = flag_;
		}

		/*
		某种原因导致谓词设置与cv.notify_one()中间隔了 wake_delay 毫秒
		*/
		Sleep(wake_delay);

		{
			std::lock_guard<std::mutex> lck(mu);
			//根据 https://en.cppreference.com/w/cpp/thread/condition_variable cv.notify_one()不需要加锁
			//但是需要给 noticed 赋值,所以一起加锁了
			cv.notify_one();
			noticed_flag = true;
		}

		th.join();
	};

	bool temp_noticed = true;
	while (1) {
		std::cout << "----------------Test Results----------------\n";

		flag = false; noticed_flag = temp_noticed;
		wake_and_wait(true, 20, 10);

		flag = false; noticed_flag = temp_noticed;
		wake_and_wait(true, 10, 20);

		flag = false; noticed_flag = temp_noticed;
		wake_and_wait(false, 20, 10);

		flag = false; noticed_flag = temp_noticed;
		wake_and_wait(false, 10, 20);
	}

	return 0;
}

解释: wake_and_wait 启动一个线程,里面进行 wait_for,超时时间是 wait_time_out ;然后把谓词 flag 设为 flag_;延时 wake_delay 毫秒后,加锁的,条件变量唤醒,并且唤醒标志位设为 true。

分两种测试情况,

一是 temp_noticed 初始化为 true,这种情况下 wait_func 无需等待唤醒,即可结束等待。程序输出:

----------------Test Results----------------
1
1
0
0

二是 temp_noticed 初始化为 false,这种情况下 wait_func 必须等待唤醒(即temp_noticed 被设为 true 时)才能结束等待。程序输出:

----------------Test Results----------------
1
0
0
0

究其原因是 wait_for(wait 同理)内部第一次判断谓词为真时则不进入等待,即使没有被唤醒。

另一种重载是

    template <class _Rep, class _Period>
    cv_status wait_for(unique_lock<mutex>& _Lck, const chrono::duration<_Rep, _Period>& _Rel_time) {
        // wait for duration
        if (_Rel_time <= chrono::duration<_Rep, _Period>::zero()) {
            return cv_status::timeout;
        }

        // TRANSITION, ABI: The standard says that we should use a steady clock,
        // but unfortunately our ABI speaks struct xtime, which is relative to the system clock.
        _CSTD xtime _Tgt;
        const bool _Clamped     = _To_xtime_10_day_clamped(_Tgt, _Rel_time);
        const cv_status _Result = wait_until(_Lck, &_Tgt);
        if (_Clamped) {
            return cv_status::no_timeout;
        }

        return _Result;
    }

不多介绍,msvc编译出来的执行结果不确定,表现为在没有任何唤醒的情况下,cv.wait_for(lck, std::chrono::milliseconds(10)) 会返回超时。可能是如下原因:

TRANSITION, ABI: The standard says that we should use a steady clock, but unfortunately our ABI speaks struct xtime, which is relative to the system clock.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值