第四章 同步并发操作(下)

4.3 限定等待时间

  • 之前介绍过的所有阻塞调用,将会阻塞一段不确定的时间,将线程挂起直到等待的事件发生
  • 一些情况下要限制一下线程等待的时间
  • 发送一些类似“我还存活”的信息,无论是对交互式用户,或是其他进程,亦或当用户放弃等待,你可以按下“取消”键直接终止等待
  • 两种超时方式:
  1. 第一种方式,需要指定一段时间(例如,30毫秒) ——称时延
  2. 第二种方式,就是指定一个时间点 ——称绝对
  • 处理持续时间的变量以“_for”作为后缀,处理绝对时间的变量以"_until"作为后缀

4.3.1 时钟

  • 时钟节拍:若每个节拍为2.5s,则 std::ratio<5, 2>
  • 当时钟节拍均匀分布(无论是否与周期匹配),并且不可调整,这种时钟就称为稳定时钟;
  • std::chrono::system_clock是不稳定的,因为时钟是可调的,代表了系统时钟的“实际时间”,并且提供了函数可将时间点转化为time_t类型的;
  • 稳定时钟对于计时非常重要,因此c++给出了稳定时钟 std::chrono::steady_clock
  • std::chrono::high_resolution_clock 可能是标准库中提供的具有最小节拍周期(因此具有最高的精度[分辨率])的时钟

4.3.2 时延

  • std::chrono::duration<>函数模板能够对时延进行处理(线程库使用到的所有C++时间处理工具,都在std::chrono命名空间内)

  • 第一个模板参数是一个类型表示——两个数相除后所的数的类型;

  • 第二个模板参数是制定部分,表示每一个单元所用秒数;
    如:std::chrono::duration<short, std::ratio<60, 1>> 代表单位是分钟

  • 在chrono空间内有已定义好的:nanoseconds[纳秒] , microseconds[微秒] , milliseconds[毫秒] , seconds[秒] , minutes[分]和hours[时]

  • 显示转换可以由std::chrono::duration_cast<>

std::chrono::milliseconds ms(54832);
std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(ms);

这里的结果就是截断的,而不是进行了舍入,所以s最后的值将为54;

std::future<int> f=std::async(some_task);
if(f.wait_for(std::chrono::milliseconds(35))==std::future_status::ready)
  do_something_with(f.get());
  • 等待函数会返回一个状态值,来表示等待是超时,还是继续等待
  • 当函数等待超时时,会返回std::future_status::timeout
  • 当“future”状态改变,函数会返回std::future_status::ready

4.3.3 时间点

  • 时钟的时间点可以用std::chrono::time_point<>的类型模板实例来表示
    第一个参数用来指定所要使用的时钟
    第二个函数参数用来表示时间的计量单位(特化的std::chrono::duration<>)
  • 时钟可能共享一个时间戳,或具有独立的时间戳。当两个时钟共享一个时间戳时,其中一个time_point类型可以与另一个时钟类型中的time_point相关联
  • 例如: std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>
  • std::chrono::hight_resolution_clock::now() + std::chrono::nanoseconds(500) 将得到500纳秒后的时间
  • 代码块的计时
auto start = std::chrono::system_time::now();
// do some thing
auto stop = std:: chrono::system_time::now();
cout << "run time is "" << std::chrono::duration<double,std::chrono::seconds>(stop-start).count() << endl;
#include <condition_variable>
#include <mutex>
#include <chrono>

std::condition_variable cv;
bool done;
std::mutex m;

bool wait_loop()
{
  auto const timeout= std::chrono::steady_clock::now()+
      std::chrono::milliseconds(500);
  std::unique_lock<std::mutex> lk(m);
  while(!done)
  {
    if(cv.wait_until(lk,timeout)==std::cv_status::timeout)
      break;
  }
  return done;
}

4.4 使用同步操作简化代码

  • 比起在多个线程间直接共享数据,每个任务拥有自己的数据会应该会更好,并且结果可以对其他线程进行广播,这就需要使用future来完成了
4.4.1 使用future的函数化编程(functional programming)
  • 术语函数化编程(functional programming)引用于一种编程方式,这种方式中的函数结果只依赖于传入函数的参数,并不依赖外部状态
  • 大多数函数都是纯粹的(共享数据没有被修改,那么就不存在条件竞争,并且没有必要使用互斥量去保护共享数据)
  • 一个future对象可以在线程间互相传递,并允许其中一个计算结果依赖于另外一个的结果,而非对共享数据的显式访问
快速排序 FP模式并行版
template<typename T>
std::list<T> parallel_quick_sort(std::list<T> input)  // 输入一个链表list,返回一个链表
{
  if(input.empty())
  {
    return input;
  }
  std::list<T> result;                                // 构建一个链表list result存储结果
  result.splice(result.begin(),input,input.begin());  // 选择input链表的第一个元素作为“中间”值
  T const& pivot=*result.begin();                     // 避免多次拷贝,使用引用

  auto divide_point=std::partition(input.begin(),input.end(),
                [&](T const& t){return t<pivot;});          //利用partition将其分为大于中间值,和小于中间值的

  std::list<T> lower_part;
  lower_part.splice(lower_part.end(),input,input.begin(),
                divide_point);

  std::future<std::list<T> > new_lower(  					// 创建一个新的线程去处理小于中间值部分的数据
                std::async(&parallel_quick_sort<T>,std::move(lower_part))); 

  auto new_higher(
                parallel_quick_sort(std::move(input)));  // 本线程中处理大于中间值的部分

  result.splice(result.end(),new_higher);  // 将大于的部分连接到resul list中
  result.splice(result.begin(),new_lower.get());  // 将小于中间值的部分连接到result list 中
  return result;
}
  • 比起使用std::async(),你可以写一个spawn_task()函数对std::packaged_task和std::thread做简单的包装
template<typename F,typename A>
std::future<std::result_of<F(A&&)>::type>
   spawn_task(F&& f,A&& a)
{
  typedef std::result_of<F(A&&)>::type result_type;
  std::packaged_task<result_type(A&&)>
       task(std::move(f)));
  std::future<result_type> res(task.get_future());
  std::thread t(std::move(task),std::move(a));
  t.detach();
  return res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值