传统意义上OS提供的并发机制包含进程和线程两个级别。考虑到实际复杂性,c++11仅提供了线程并发机制。

c++11提供的线程并发机制主要位于四个头文件中:、、、<condition_variable>、。

线程并发机制包括线程管理、原子操作、线程同步对象。

线程管理

c++11中将可并发执行的运算成为一个任务(task),在OS的线程模型中,一个任务就是一个线程,实际需要在创建时指定线程函数。c++基于此提供了更为优雅的线程处理模型,不需要关心类型转换和指针的处理,所有线程管理的模型是基于std::thread实现的。在c++中一个任务通常是指函数、函数对象或Lamda表达式。比如下面代码:

// thread util sample 1
#include <iostream>
#include <thread>
using namespace std; void TaskFunc()
{} class TaskObj
{
public:
TaskObj(){}
void operator()()
{}
}; int main(int argc, char ** argv)
{
thread t_func{TaskFunc};
thread t_obj{TaskObj()}; t_func.join();
t_obj.join();
return 0;
}

创建thread对象时,就是线程启动的时候;thread::join()函数用于等待线程函数执行完成。

线程启动时的参数传递

上面代码(sample 1)中线程函数没有参数,如果需要给线程函数传递参数可以参考下面代码(参数类型可按值传递、按指针传递或按类型传递)。

// thread util sample 2
void TaskFunc(int i)
{} class TaskObj
{
public:
TaskObj(int i):m_value(i){}
void operator()()
{m_value=123;}
private:
int m_value{0};
}; int main(int argc, char ** argv)
{
int cur_value{100};
thread t_func{TaskFunc, cur_value};
thread t_obj{TaskObj{cur_value}}; t_func.join();
t_obj.join();
return 0;
}

原子操作

操作系统中轻量级的线程同步机制通常是原子操作,c++11提供了相应机制,所有文件位于中。主要包含std::atomic模板类和std::atomic_flag类。

具体的建议参考atomic header。下面是一个说明int自增的原子函数

// atomic::operator++ example
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread
#include <vector> // std::vector std::atomic<int> ready{0}; void AtomicIncreasement (int id)
{
++ready;
}; int main(int argc, char** argv)
{
std::vector<std::thread> threads;
std::cout << "spawning 5 threads that do incereasement...\n";
for (int i=1; i<=5; ++i) threads.push_back(std::thread(AtomicIncreasement,i));
for (auto& th : threads) th.join(); std::cout << ready << std::endl; return 0;
}

输出如下:

spawning 5 threads that do incereasement...

5

线程同步对象

c++11中提供了两种线程同步机制,mutex和condition_variable,分别对应Windows同步机制中的互斥量和事件。当然,c++中也对各个部分做了详细划分,以mutex为例,分为以下四种(位于头文件中):

  • mutex
  • recursive_mutex
  • timed_mutex
  • recursive_timed_mutex

同时也提供了辅助的机制,比如unique_lock、lock_guard用于实现自动锁定和释放mutex。

比如下面例子说明了mutex的调用逻辑:

// mutex example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex std::mutex mtx; // mutex for critical section void print_block(int n, char c)
{
// critical section (exclusive access to std::cout signaled by locking mtx):
mtx.lock();
for (int i=0; i<n; ++i) { std::cout << c; }
std::cout << '\n';
mtx.unlock();
} void print_block_auto(int n, char c)
{
// critical section (exclusive access to std::cout signaled by locking mtx):
std::unique_lock<std::mutex> lck{mtx};
for (int i=0; i<n; ++i) { std::cout << c; }
std::cout << '\n';
} int main ()
{
std::thread th1{print_block, 50, '*'};
std::thread th2{print_block_auto, 50, '$'}; th1.join();
th2.join(); return 0;
}

针对windows下的WaitForMultipleObject函数,c++提供了defer_lcok和lock(可变参数模块),用于实现同时等待多个同步对象。

关于condition_variable的介绍,建议参考<condition_variable>

线程返回参数处理

传统的线程返回值,可以通过指针和引用处理,但如何实现类似windows下线程函数返回值的处理逻辑,c++也提供了这种机制,相关机制位于头文件中(http://www.cplusplus.com/reference/future/)。promise和packaged_task作为数据载体,future作为数据接收者。

下面是两个说明future和promise、packaged_task的使用。

// promise example
#include <iostream> // std::cout
#include <functional> // std::ref
#include <thread> // std::thread
#include <future> // std::promise, std::future void print_int (std::future<int>& fut) {
int x = fut.get();
std::cout << "value: " << x << '\n';
} int main ()
{
std::promise<int> prom; // create promise std::future<int> fut = prom.get_future(); // engagement with future std::thread th1 (print_int, std::ref(fut)); // send future to new thread prom.set_value (10); // fulfill promise
// (synchronizes with getting the future)
th1.join();
return 0;
}
// packaged_task example
#include <iostream> // std::cout
#include <future> // std::packaged_task, std::future
#include <chrono> // std::chrono::seconds
#include <thread> // std::thread, std::this_thread::sleep_for // count down taking a second for each value:
int countdown (int from, int to) {
for (int i=from; i!=to; --i) {
std::cout << i << '\n';
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "Lift off!\n";
return from-to;
} int main ()
{
std::packaged_task<int(int,int)> tsk (countdown); // set up packaged_task
std::future<int> ret = tsk.get_future(); // get future std::thread th (std::move(tsk),10,0); // spawn thread to count down from 10 to 0 // ... int value = ret.get(); // wait for the task to finish and get result std::cout << "The countdown lasted for " << value << " seconds.\n"; th.join(); return 0;
}

附加说明

c++11也提供了其他机制比如thread_local线程局部变量的存储限定符、once_flag用于标识仅初始化一次的处理逻辑以及简化版的async()函数——支持局部语法的并行化。

本文涉及的代码可以直接从我的git下载:https://git.oschina.net/Tocy/SampleCode.git ,位于c++11目录下,名字前缀为thread_util_sample*.cpp。

c++11并发机制的更多相关文章

  1. C++11并发内存模型学习

    C++11标准已发布多年,编译器支持也逐渐完善,例如ms平台上从vc2008 tr1到vc2013.新标准对C++改进体现在三方面:1.语言特性(auto,右值,lambda,foreach):2.标 ...

  2. 【Java基础】线程和并发机制

    前言 在Java中,线程是一个很关键的名词,也是很高频使用的一种资源.那么它的概念是什么呢,是如何定义的,用法又有哪些呢?为何说Android里只有一个主线程呢,什么是工作线程呢.线程又存在并发,并发 ...

  3. Java并发机制和底层实现原理

    Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码转化为汇编指令在CPU上执行.Java中的并发机制依赖于JVM的实现和CPU的指令. Java语言规范第三版中 ...

  4. 《Java并发编程的艺术》读书笔记:二、Java并发机制的底层实现原理

    二.Java并发机制底层实现原理 这里是我的<Java并发编程的艺术>读书笔记的第二篇,对前文有兴趣的朋友可以去这里看第一篇:一.并发编程的目的与挑战 有兴趣讨论的朋友可以给我留言! 1. ...

  5. C++11 并发指南后续更新

    C++11 并发指南的第一篇是 2013 年 8 月 3 号写的,到今天(2013 年 8 月 31 号)差不多一个月了,前前后后共写了 6 章(目前共 8 篇)博客介绍 C++11 的并发编程,但还 ...

  6. C++11 并发指南系列

    本系列文章主要介绍 C++11 并发编程,计划分为 9 章介绍 C++11 的并发和多线程编程,分别如下: C++11 并发指南一(C++11 多线程初探)(本章计划 1-2 篇,已完成 1 篇) C ...

  7. C++11 并发指南三(Lock 详解)

    在 <C++11 并发指南三(std::mutex 详解)>一文中我们主要介绍了 C++11 标准中的互斥量(Mutex),并简单介绍了一下两种锁类型.本节将详细介绍一下 C++11 标准 ...

  8. C++11 并发指南六(atomic 类型详解四 C 风格原子操作介绍)

    前面三篇文章<C++11 并发指南六(atomic 类型详解一 atomic_flag 介绍)>.<C++11 并发指南六( <atomic> 类型详解二 std::at ...

  9. C++11 并发指南六(atomic 类型详解三 std::atomic (续))

    C++11 并发指南六( <atomic> 类型详解二 std::atomic ) 介绍了基本的原子类型 std::atomic 的用法,本节我会给大家介绍C++11 标准库中的 std: ...

随机推荐

  1. 我的第一个Raspberry PI装置

    好吧,我先承认是这是从书上学习的第一个示例. 我入手的是Raspberry红版,版本2,看到这么小巧的卡片电脑可以做这么多事情,真是让人惊喜! 安装系统等都很顺利,启动.安装程序.远程桌面.SSH登录 ...

  2. maven-compiler-plugin升级到3.1出现问题(转)

    转自:http://my.oschina.net/zhuka/blog/124503 No compiler is provided in this environment. Perhaps you ...

  3. Android之Activity系列总结(三)--Activity的四种启动模式

    一.返回栈简介 任务是指在执行特定作业时与用户交互的一系列 Activity. 这些 Activity 按照各自的打开顺序排列在堆栈(即返回栈,也叫任务栈)中. 首先介绍一下任务栈: (1)程序打开时 ...

  4. SQL Server 表分区之水平表分区

    什么是表分区? 表分区分为水平表分区和垂直表分区,水平表分区就是将一个具有大量数据的表,进行拆分为具有相同表结构的若干个表:而垂直表分区就是把一个拥有多个字段的表,根据需要进行拆分列,然后根据某一个字 ...

  5. Bitter Sweet Symphony

    当我写下这段话时,另一个我觉醒了. 时间仿佛一下子从2013年的末尾跳到了2014年了,是那么的猝不及防.1990——2014,24岁了,一瞬间,不知不觉已经走过了24个岁月了.过去,我时常会反省着, ...

  6. U811.1接口EAI系列之六--物料上传--VB语言

    1. 业务系统同步U811.1存货档案通用方法. 2.具体代码处理如下: 作者:王春天 2013-11-06 地址:http://www.cnblogs.com/spring_wang/p/34098 ...

  7. Logstash+ElasticSearch+Kibana处理nginx访问日志(转)

    ELK似乎是当前最为流行的日志收集-存储-分析的全套解决方案. 去年年初, 公司里已经在用, 当时自己还山寨了一个统计系统(postgresql-echarts, 日志无结构化, json形式存储到p ...

  8. 创建多模块springcloud应用eureka server和client和消费端demo

    使用环境是 STS + maven 1 创建父级 项目,springcloud-demo1 new -> maven project -> 按照要求进行配置即可.然后删除 src目录,因为 ...

  9. scala中json与对象的转换

    遇到的问题 因为要把spark从es读出来的json数据转换为对象,开始想用case class定义类型,通过fastjson做转换.如下 case class Book (author: Strin ...

  10. 如何设置Linux swap分区

    看到不少朋友问linux下swap分区的问题,收集到一篇 不错的文章 ,分享下.什么是Swap?Swap,即交换区,除了安装Linux的时候,有多少人关心过它呢?其实,Swap的调整对Linux服务器 ...