c++11新特性多线程操作实战

c++11多线程操作

线程

thread

int main()
{
  thread t1(Test1);
  t1.join();
  thread t2(Test2);
  t2.join();
  thread t3 = t1;
  thread t4(t1);
  thread t5 = std::move(t1);
  thread t6(std::move(t1));
  return 0;
}

t3,t4创建失败,因为thread的拷贝构造和赋值运算符重载的原型是:

thread(const thread&) = delete;
thread& operator=(const thread&) = delete;

被禁用了,但是t5, t6线程是创建成功的。std::move把t1转换为右值,调用的是函数原型为thread& operator=(thread&& _Other) noexceptthread(thread&& _Other) noexcept

当线程对象t1被移动拷贝和移动赋值给t5和t6的时候,t1就失去了线程控制权,也就是一个线程只能同时被一个线程对象所控制。最直观的是t1.joinable()返回值为false,joinable()函数后面介绍。

使用类成员函数作为线程参数:

class Task
{
public:
  Task(){}
  void Task1() {}
  void Task2() {}
private:
};

int main()
{
  Task task;
  thread t3(&Task::Task1, &task);
  t3.join();
  return 0;
}

关键点是要创建一个类对象,并作为第二个参数传入thread()线程的构造函数中去。

管理当前线程的函数

yield

此函数的准确性为依赖于实现,特别是使用中的 OS 调度器机制和系统状态。例如,先进先出实时调度器( Linux 的 SCHED_FIFO )将悬挂当前线程并将它放到准备运行的同优先级线程的队列尾(而若无其他线程在同优先级,则 yield 无效果)。

#include <iostream>
#include <chrono>
#include <thread>

// 建议其他线程运行一小段时间的“忙睡眠”
void little_sleep(std::chrono::microseconds us)
{
  auto start = std::chrono::high_resolution_clock::now();
  auto end = start + us;
  do {
    std::this_thread::yield();
  } while (std::chrono::high_resolution_clock::now() < end);
}

int main()
{
  auto start = std::chrono::high_resolution_clock::now();

  little_sleep(std::chrono::microseconds(100));

  auto elapsed = std::chrono::high_resolution_clock::now() - start;
  std::cout << "waited for "
       << std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count()
       << " microseconds\n";
}

get_id

这个函数不用过多介绍了,就是用来获取当前线程id的,用来标识线程的身份。

std::thread::id this_id = std::this_thread::get_id();

sleep_for

位于this_thread命名空间下,msvc下支持两种时间参数。

std::this_thread::sleep_for(2s);
std::this_thread::sleep_for(std::chrono::seconds(1));

sleep_untile

参数构建起来挺麻烦的,一般场景下要求线程睡眠的就用sleep_for就行了

using std::chrono::system_clock;
time_t tt = system_clock::to_time_t(system_clock::now());
struct std::tm *ptm = localtime(&tt);
 std::this_thread::sleep_until(system_clock::from_time_t(mktime(ptm)));

互斥

mutex

对于互斥量看到一个很好的比喻:

单位上有一台打印机(共享数据a),你要用打印机(线程1要操作数据a),同事老王也要用打印机(线程2也要操作数据a),但是打印机同一时间只能给一个人用,此时,规定不管是谁,在用打印机之前都要向领导申请许可证(lock),用完后再向领导归还许可证(unlock),许可证总共只有一个,没有许可证的人就等着在用打印机的同事用完后才能申请许可证(阻塞,线程1lock互斥量后其他线程就无法lock,只能等线程1unlock后,其他线程才能lock),那么,这个许可证就是互斥量。互斥量保证了使用打印机这一过程不被打断。

代码示例:

mutex mtx;

int gNum = 0;
void Test1()
{
  mtx.lock();
  for(int n = 0; n < 5; ++n)
    gNum++;
  mtx.unlock();
}

void Test2()
{
  std::cout << "gNum = " << gNum << std::endl;
}

int main()
{
  thread t1(Test1);
  t1.join();
  thread t2(Test2);
  t2.join();
  return 0;
}

join()表示主线程等待子线程结束再继续执行,如果我们的期望是打印循环自增之后的gNum的值,那t1.join()就放在t2创建之前调用。因为t2的创建就标志着t2线程创建好然后开始执行了。

通常mutex不单独使用,因为lock和unlock必须配套使用,如果忘记unlock很可能造成死锁,即使unlock写了,但是如果在执行之前程序捕获到异常,也还是一样会死锁。如何解决使用mutex造成的死锁问题呢?下面介绍unique_gard和lock_guard的时候详细说明。

timed_mutex
提供互斥设施,实现有时限锁定

recursive_mutex
提供能被同一线程递归锁定的互斥设施

recursive_timed_mutex
提供能被同一线程递归锁定的互斥设施,并实现有时限锁定

通用互斥管理

lock_guard

void Test1()
{
  std::lock_guard<std::mutex> lg(mtx);
  for(int n = 0; n < 5; ++n)
  {
    gNum++;
    std::cout << "gNum = " << gNum << std::endl;
  }
}
int main()
{
  thread t1(Test1);
  thread t2(Test1);
  t1.join();
  t2.join();
  return 0;
}

lock_guard相当于利用RAII机制(“资源获取就是初始化”)把mutex封装了一下,在构造中lock,在析构中unlock。避免了中间过程出现异常导致的mutex不能够正常unlock.

  • scoped_lock(c++17)
  • unique_lock
  • defer_lock_t
  • try_to_lock_t
  • adopt_lock_t
  • defer_lock
  • try_to_lock
  • adopt_lock

通用锁算法

  • try_lock
  • lock

单次调用

  • once_flag
  • call_once

条件变量

  • condition_variable
  • condition_variable_any
  • notify_all_at_thread_exit
  • cv_status

Future

  • promise
  • packaged_task
  • future
  • shared_future
  • async
  • launch
  • future_status
  • Future错误
    • future_error
    • future_category
    • future_errc

到此这篇关于c++11新特性多线程操作实战的文章就介绍到这了,更多相关c++11 多线程操作内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++11 并发指南之多线程初探

    C++11 自2011年发布以来已经快两年了,之前一直没怎么关注,直到最近几个月才看了一些 C++11 的新特性,今后几篇博客我都会写一些关于 C++11 的特性,算是记录一下自己学到的东西吧,和大家共勉. 相信 Linux 程序员都用过 Pthread, 但有了 C++11 的 std::thread 以后,你可以在语言层面编写多线程程序了,直接的好处就是多线程程序的可移植性得到了很大的提高,所以作为一名 C++ 程序员,熟悉 C++11 的多线程编程方式还是很有益处的. 如果你对 C++11

  • c++11&14-多线程要点汇总

    在C++11以前,C++的多线程编程均需依赖系统或第三方接口实现,一定程度上影响了代码的移植性.C++11中,引入了boost库中的多线程部分内容,形成C++标准,形成标准后的boost多线程编程部分接口基本没有变化,这样方便了以前使用boost接口开发的使用者切换使用C++标准接口,很容易把boost接口升级为C++标准接口. 我们通过如下几部分介绍C++11多线程方面的接口及使用方法. 1. std::thread std::thread为C++11的线程类,使用方法和boost接口一样,非

  • C++11并发编程:多线程std::thread

    一:概述 C++11引入了thread类,大大降低了多线程使用的复杂度,原先使用多线程只能用系统的API,无法解决跨平台问题,一套代码平台移植,对应多线程代码也必须要修改.现在在C++11中只需使用语言层面的thread可以解决这个问题. 所需头文件<thread> 二:构造函数 1.默认构造函数 thread() noexcept 一个空的std::thread执行对象 2.初始化构造函数 template<class Fn, class... Args> explicit th

  • c++11新特性多线程操作实战

    c++11多线程操作 线程 thread int main() { thread t1(Test1); t1.join(); thread t2(Test2); t2.join(); thread t3 = t1; thread t4(t1); thread t5 = std::move(t1); thread t6(std::move(t1)); return 0; } t3,t4创建失败,因为thread的拷贝构造和赋值运算符重载的原型是: thread(const thread&) = d

  • c++11 新特性——智能指针使用详解

    c++11添加了新的智能指针,unique_ptr.shared_ptr和weak_ptr,同时也将auto_ptr置为废弃(deprecated). 但是在实际的使用过程中,很多人都会有这样的问题: 不知道三种智能指针的具体使用场景 无脑只使用shared_ptr 认为应该禁用raw pointer(裸指针,即Widget*这种形式),全部使用智能指针 初始化方法 class A { public: A(int size){ this->size = size; } A(){} void Sh

  • C++11新特性中auto 和 decltype 区别和联系

    C++11新特性中auto 和 decltype 区别和联系 一. auto简介 编程时候常常需要把表达式的值付给变量,需要在声明变量的时候清楚的知道变量是什么类型.然而做到这一点并非那么容易(特别是模板中),有时候根本做不到.为了解决这个问题,C++11新标准就引入了auto类型说明符,用它就能让编译器替我们去分析表达式所属的类型.和原来那些只对应某种特定的类型说明符(例如 int)不同.auto 让编译器通过初始值来进行类型推演.从而获得定义变量的类型,所以说 auto 定义的变量必须有初始

  • 详解c++11新特性之模板的改进

    C++11关于模板有一些细节的改进: 模板的右尖括号 模板的别名 函数模板的默认模板参数 模板的右尖括号 C++11之前是不允许两个右尖括号出现的,会被认为是右移操作符,所以需要中间加个空格进行分割,避免发生编译错误. int main() { std::vector<std::vector<int>> a; // error std::vector<std::vector<int> > b; // ok } 这个我之前都不知道,我开始学编程的时候就已经是C

  • C++11新特性“=default”,“=delete”的使用

    1. =default 和=delete 概述 任何事物的出现都必然有着其出现的理由,伴随着每一个新的概念产生都会带来一系列的便利和价值.C++在不断的演变与发展,与此同时,伴随着许多新的特性和功能产生.=default.=delete 是C++11的新特性,分别为:显式缺省(告知编译器生成函数默认的缺省版本)和显式删除(告知编译器不生成函数默认的缺省版本).C++11中引进这两种新特性的目的是为了增强对"类默认函数的控制",从而让程序员更加精准地去控制默认版本的函数.其具体的功能和使

  • C++11新特性之列表初始化的具体使用

    目录 统一的初始化方法 列表初始化的一些使用细节 初始化列表 1.任何长度的初始化列表 2.std::initialzer-list的使用细节 列表初始化防止类型收窄 在我们实际编程中,我们经常会碰到变量初始化的问题,对于不同的变量初始化的手段多种多样,比如说对于一个数组我们可以使用 int arr[] = {1,2,3}的方式初始化,又比如对于一个简单的结构体: struct A { int x; int y; }a={1,2}; 这些不同的初始化方法都有各自的适用范围和作用,且对于类来说不能

  • C#11新特性使用案例详解

    目录 前言 新特性之原始字符串 使用案例 原始字符串使用需要注意的地方 什么情况下需要超过三个双引号开头 尾引号和尾引号前面的换行符不包括在最终内容中 结尾的三个引号不另起一行行不行 和内插字符一起使用 总结 前言 在日常开发中我们经常会将JSON.XML.HTML.SQL.Regex等字符串拷贝粘贴到我们的代码中,而这些字符串往往包含很多的引号",我们就必须将所有引号逐个添加转义符\进行转义.这个转义十分麻烦,且容易出错,而当我们们需要替换这些字符串时,重新粘贴进来的文本仍需要再次进行转义,简

  • 浅析C++11新特性的Lambda表达式

    lambda简介 熟悉Python的程序员应该对lambda不陌生.简单来说,lambda就是一个匿名的可调用代码块.在C++11新标准中,lambda具有如下格式: [capture list] (parameter list) -> return type { function body } 可以看到,他有四个组成部分: 1.capture list: 捕获列表 2.parameter list: 参数列表 3.return type: 返回类型 4.function body: 执行代码

  • C++ 11新特性之大括号初始化详解

    本文主要给大家介绍了关于C++11新特性之大括号初始化的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: C++11之前,C++主要有以下几种初始化方式: //小括号初始化 string str("hello"); //等号初始化 string str="hello"; //大括号初始化 struct Studnet{ char* name; int age; }; Studnet s={"dablelv",18}; //

  • C++11新特性之自定义字面量

    1.示例 C++11新标准中引入了用户自定义字面量,也叫自定义后缀操作符,即通过实现一个后缀操作符,将申明了该后缀标识的字面量转化为需要的类型.考察如下代码: long double operator"" _mm(long double x) { return x / 1000; } long double operator"" _m(long double x) { return x; } long double operator"" _km(

随机推荐