c++11多线程操作

  • 线程

    • thread
    1. int main()
    2. {
    3. thread t1(Test1);
    4. t1.join();
    5. thread t2(Test2);
    6. t2.join();
    7. thread t3 = t1;
    8. thread t4(t1);
    9. thread t5 = std::move(t1);
    10. thread t6(std::move(t1));
    11. return 0;
    12. }

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

    1. thread(const thread&) = delete;
    2. 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()函数后面介绍。

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

    1. class Task
    2. {
    3. public:
    4. Task(){}
    5. void Task1() {}
    6. void Task2() {}
    7. private:
    8. };
    9. int main()
    10. {
    11. Task task;
    12. thread t3(&Task::Task1, &task);
    13. t3.join();
    14. return 0;
    15. }

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

  • 管理当前线程的函数

    • yield

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

    1. #include <iostream>
    2. #include <chrono>
    3. #include <thread>
    4. // 建议其他线程运行一小段时间的“忙睡眠”
    5. void little_sleep(std::chrono::microseconds us)
    6. {
    7. auto start = std::chrono::high_resolution_clock::now();
    8. auto end = start + us;
    9. do {
    10. std::this_thread::yield();
    11. } while (std::chrono::high_resolution_clock::now() < end);
    12. }
    13. int main()
    14. {
    15. auto start = std::chrono::high_resolution_clock::now();
    16. little_sleep(std::chrono::microseconds(100));
    17. auto elapsed = std::chrono::high_resolution_clock::now() - start;
    18. std::cout << "waited for "
    19. << std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count()
    20. << " microseconds\n";
    21. }
    • get_id

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

    1. std::thread::id this_id = std::this_thread::get_id();
    • sleep_for

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

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

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

    1. using std::chrono::system_clock;
    2. time_t tt = system_clock::to_time_t(system_clock::now());
    3. struct std::tm *ptm = localtime(&tt);
    4. std::this_thread::sleep_until(system_clock::from_time_t(mktime(ptm)));
  • 互斥

    • mutex

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

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

    代码示例:

    1. mutex mtx;
    2. int gNum = 0;
    3. void Test1()
    4. {
    5. mtx.lock();
    6. for(int n = 0; n < 5; ++n)
    7. gNum++;
    8. mtx.unlock();
    9. }
    10. void Test2()
    11. {
    12. std::cout << "gNum = " << gNum << std::endl;
    13. }
    14. int main()
    15. {
    16. thread t1(Test1);
    17. t1.join();
    18. thread t2(Test2);
    19. t2.join();
    20. return 0;
    21. }

    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
    1. void Test1()
    2. {
    3. std::lock_guard<std::mutex> lg(mtx);
    4. for(int n = 0; n < 5; ++n)
    5. {
    6. gNum++;
    7. std::cout << "gNum = " << gNum << std::endl;
    8. }
    9. }
    10. int main()
    11. {
    12. thread t1(Test1);
    13. thread t2(Test1);
    14. t1.join();
    15. t2.join();
    16. return 0;
    17. }

    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 新特性实战 (一):多线程操作的更多相关文章

  1. c++11新特性实战(二):智能指针

    c++11添加了新的智能指针,unique_ptr.shared_ptr和weak_ptr,同时也将auto_ptr置为废弃(deprecated). 但是在实际的使用过程中,很多人都会有这样的问题: ...

  2. [转载] C++11新特性

    C++11标准发布已有一段时间了, 维基百科上有对C++11新标准的变化和C++11新特性介绍的文章. 我是一名C++程序员,非常想了解一下C++11. 英文版的维基百科看起来非常费劲,而中文版维基百 ...

  3. c++学习书籍推荐《深入理解C++11 C++11新特性解析与应用》下载

    百度云及其他网盘下载地址:点我 编辑推荐 <深入理解C++11:C++11新特性解析与应用>编辑推荐:C++标准委员会成员和IBM XL编译器中国开发团队共同撰写,权威性毋庸置疑.系统.深 ...

  4. c++ 11 线程池---完全使用c++ 11新特性

    前言: 目前网上的c++线程池资源多是使用老版本或者使用系统接口实现,使用c++ 11新特性的不多,最近研究了一下,实现一个简单版本,可实现任意任意参数函数的调用以及获得返回值. 0 前置知识 首先介 ...

  5. C++11新特性总结 (二)

    1. 范围for语句 C++11 引入了一种更为简单的for语句,这种for语句可以很方便的遍历容器或其他序列的所有元素 vector<int> vec = {1,2,3,4,5,6}; ...

  6. C++11新特性总结 (一)

    1. 概述 最近在看C++ Primer5 刚好看到一半,总结一下C++11里面确实加了很多新东西,如果没有任何了解,别说自己写了,看别人写的代码估计都会有些吃力.C++ Primer5是学习C++1 ...

  7. 在C++98基础上学习C++11新特性

    自己一直用的是C++98规范来编程,对于C++11只闻其名却没用过其特性.近期因为工作的需要,需要掌握C++11的一些特性,所以查阅了一些C++11资料.因为自己有C++98的基础,所以从C++98过 ...

  8. C++11新特性——range for

    很多编程语言都有range for语法功能,自C++11起,终于将这个重要功能加入C++标准中.range for语句,可以方便的遍历给定序列中的每个元素并对其执行某种操作. 1.基本语法 for(d ...

  9. C++11 新特性之智能指针(shared_ptr, unique_ptr, weak_ptr)

    这是C++11新特性介绍的第五部分,涉及到智能指针的相关内容(shared_ptr, unique_ptr, weak_ptr). shared_ptr shared_ptr 基本用法 shared_ ...

随机推荐

  1. MPI中的cannon算法

    Cannon算法 算法过程 假设矩阵\(A,B\)和\(C\)都可以分成\(m\times m\)块矩阵,即\(A = (A_{(ij)})_{m\times m},B = (B_{(ij)})_{m ...

  2. 漏洞重温之sql注入(五)

    漏洞重温之sql注入(五) sqli-labs通关之旅 填坑来了! Less-17 首先,17关,我们先查看一下页面,发现网页正中间是一个登录框. 显然,该关卡的注入应该为post型. 直接查看源码. ...

  3. Javascript循环和代码规范

    1 - 循环 1.1 for循环 语法结构 for(初始化变量; 条件表达式; 操作表达式 ){ //循环体 } 名称 作用 初始化变量 通常被用于初始化一个计数器,该表达式可以使用 var 关键字声 ...

  4. linux网络配置及虚拟机连接不上网排错思路

    第1章          操作系统与虚拟软件的使用 1.1  虚拟软件使用方法 Vmware 1.1.1  开启vmware 注: 同时只能开启一个VMware软件,如果开了两个VMware窗口 提示 ...

  5. gdb我在我本机上p不了,在别人机子上可以

    gdb我在我本机上p不了,在别人机子上可以,不知道什么 (gdb) p EventFlow->devicetypeThere is no member or method named devic ...

  6. 领导给了一堆无序杂乱的数据,我写了个Python自动化脚本

    这个问题算是群友答疑.如果说同事或者老板给你一堆这样的数据,你估计会抓狂,该怎么处理呢? 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. ...

  7. 实际项目中遇到EF实体类的操作问题及操作方法

    之前一直做ASP,都是直接写数据库操作语句,但是现在使用linq或者EF了,具体数据库操作不会了,遇到几个问题,然后经过查找资料解决了,记录一下. 一.遇到序列化问题 遇到循环引用问题,我的项目是一个 ...

  8. iptables实用知识 ,一文学会配置linux防火墙

    目录 1.防火墙的概念 2. linux防火墙 3.linux数据包处理流程 3.1 linux 防火墙将以上流程,固定区分为5个流程节点 3.2 数据流程 4 linux防火墙的实现机制 4.1 i ...

  9. 转载:Java的三种取整办法

    转载地址:https://blog.csdn.net/maple_fix/article/details/78656152 方法一:向上取整Math.ceil();举例:Math.ceil(11.4) ...

  10. Agumaster 增加雪球网爬虫