C++11多线程教学II
从我最近发布的C++11线程教学文章里,我们已经知道C++11线程写法与POSIX的pthreads写法相比,更为简洁。只需很少几个简单概念,我们就能搭建相当复杂的处理图片程序,但是我们回避了线程同步的议题。在接下来的部分,我们将进入C++11多线程编程的同步领域,看看如何来同步一组并行的线程。
我们快速回顾一下如何利用c++11创建线程组。上次教学当中,我们用传统c数组保存线程,也完全可以用标准库的向量容器,这样做更有c++11的气象,同时又能避免使用new和delete来动态分配内存所带来的隐患。
#include
#include
#include
//This function will be called from a thread线程将调用此函数
void func(int tid) {
std::cout << "Launched by thread " << tid << std::endl;
}
int main() {
std::vectorth;
int nr_threads = 10;
//Launch a group of threads 启动一组线程
for (int i = 0; i < nr_threads; ++i) {
th.push_back(std::thread(func,i));
}
//Join the threads with the main thread 与主线程协同运转
for(auto &t : th){
t.join();
}
return 0;
}
在Mac OSX Lion上用clang++或gcc-4.7编译上述程序:
clang++ -Wall -std=c++0x -stdlib=libc++ file_name.cpp
g++-4.7 -Wall -std=c++11 file_name.cpp
现代Linux系统上,使用gcc-4.6.x编译代码:
g++ -std=c++0x -pthread file_name.cpp
某些活生生的现实问题,其棘手的地方就在于它们天然就是并行方式,上面开头部分写的代码当中,已用很简化的语法实现了这种方式。举一个典型的并行问题:引入两个数组,一个数组与乘数相乘,生成孟得伯特集合。
线程之间还有同步层次的问题。以向量点乘为例来说,两个等长(维度)向量,他们的元素两两对应相乘,然后乘积相加得到一个标量结果。初略的并行编码方式如下:
#include
#include
#include
...
void dot_product(const std::vector&v1, const std::vector&v2, int &result,
int L, int R){
for(int i = L; i < R; ++i){
result += v1[i] * v2[i];
}
}
int main(){
int nr_elements = 100000;
int nr_threads = 2;
int result = 0;
std::vectorthreads;
//Fill two vectors with some constant values for a quick verification
// v1={1,1,1,1,...,1}以常量值填充两个向量,便于检验
// v2={2,2,2,2,...,2}
// The result of the dot_product should be 200000 for this particular case
//当前例子的点乘结果应为200000
std::vectorv1(nr_elements,1), v2(nr_elements,2);
//Split nr_elements into nr_threads parts 把nr_elements份计算任务划分为 nr_threads 个部分
std::vectorlimits = bounds(nr_threads, nr_elements);
//Launch nr_threads threads: 启动 nr_threads 条线程
for (int i = 0; i < nr_threads; ++i) {
threads.push_back(std::thread(dot_product, std::ref(v1), std::ref(v2),
std::ref(result), limits[i], limits[i+1]));
}
//Join the threads with the main thread 协同 线程组与主线程
for(auto &t : threads){
t.join();
}
//Print the result打印结果
std::cout<<result<<std::endl;
return 0;
}
上述代码的结果显然应该是200000,但是运行几次出来的结果都有轻微的差异:
sol $g++-4.7 -Wall -std=c++11 cpp11_threads_01.cpp
sol $./a.out
138832
sol $./a.out
138598
sol $./a.out
138032
sol $./a.out
140690
sol $
怎么回事?仔细看第九行代码,变量result累加v1[i],v2[i]之和。该行是典型的竞争条件,这段代码在两个异步线程中并行运作,变量result可以被任意一方抢先访问而被改变。
通过规定该变量应同步地由线程来访问,我们可以避免出问题,我们可以采用一个mutex(互斥)来达成目的,mutex是一种特别用途的变量,行为如同一个barrier,同步化访问那段修改result变量的代码:
#include
#include
#include
#include
static std::mutex barrier;
...
void dot_product(const std::vector&v1, const std::vector&v2, int &result, int L, int R){
int partial_sum = 0;
for(int i = L; i < R; ++i){
partial_sum += v1[i] * v2[i];
}
std::lock_guardblock_threads_until_finish_this_job(barrier);
result += partial_sum;
}
...
第6行创建一个全局mutex变量barrier,第15行强制线程在完成for循环之后才同步存取result。注意,这一次我们采用了新的变量partial sum,声明为线程局部变量。其他代码部分保持原貌。
针对这个特定的例子,我们还可以找到更简洁优美的方案,我们可以采用原子类型,这是一种特定的变量类型,能达成安全的同时读写,在底层基本上解决了同步问题。额外注明一下,我们可以使用的原子类型只能用在原子操作上,这些操作都定义在atomic 头文件里面:
#include
#include
#include
#include
void dot_product(const std::vector&v1, const std::vector&v2, std::atomic&result, int L, int R){
int partial_sum = 0;
for(int i = L; i < R; ++i){
partial_sum += v1[i] * v2[i];
}
result += partial_sum;
}
int main(){
int nr_elements = 100000;
int nr_threads = 2;
std::atomicresult(0);
std::vectorthreads;
...
return 0;
}
苹果机的clang++当前还不支持原子类型和原子操作,有两个办法可以达到目标,编译最新clang++源码,要么使用最新的gcc-4.7,也需要编译源码。
想学习c++11新语法,我推荐阅读《Professional C++》第二版,《C++ Primer Plus》也可以。
C++11多线程教学II的更多相关文章
- C++11多线程教学(二)
C++11多线程教学II 从我最近发布的C++11线程教学文章里,我们已经知道C++11线程写法与POSIX的pthreads写法相比,更为简洁.只需很少几个简单概念,我们就能搭建相当复杂的处理图片程 ...
- C++11多线程教学(一)
本篇教学代码可在GitHub获得:https://github.com/sol-prog/threads. 在之前的教学中,我展示了一些最新进的C++11语言内容: 1. 正则表达式(http://s ...
- c++ 11 多线程教学(1)
本篇教学代码可在GitHub获得:https://github.com/sol-prog/threads. 在之前的教学中,我展示了一些最新进的C++11语言内容: 1. 正则表达式(http://s ...
- C++11多线程教学
转自:http://www.cnblogs.com/lidabo/p/3908705.html 本篇教学代码可在GitHub获得:https://github.com/sol-prog/threads ...
- C++11 多线程 教学(2)
C++11开始支持多线程编程,之前多线程编程都需要系统的支持,在不同的系统下创建线程需要不同的API如pthread_create(),Createthread(),beginthread()等, ...
- 【转】C++ 11 并发指南一(C++ 11 多线程初探)
引言 C++ 11自2011年发布以来已经快两年了,之前一直没怎么关注,直到最近几个月才看了一些C++ 11的新特性,算是记录一下自己学到的东西吧,和大家共勉. 相信Linux程序员都用过Pthrea ...
- C++11 并发指南一(C++11 多线程初探)
引言 C++11 自2011年发布以来已经快两年了,之前一直没怎么关注,直到最近几个月才看了一些 C++11 的新特性,今后几篇博客我都会写一些关于 C++11 的特性,算是记录一下自己学到的东西吧, ...
- C++11 多线程编程 使用lambda创建std::thread (生产/消费者模式)
要写个tcp server / client的博客,想着先写个c++11多线程程序.方便后面写博客使用. 目前c++11中写多线程已经很方便了,不用再像之前的pthread_create,c++11中 ...
- C++11 并发指南九(综合运用: C++11 多线程下生产者消费者模型详解)
前面八章介绍了 C++11 并发编程的基础(抱歉哈,第五章-第八章还在草稿中),本文将综合运用 C++11 中的新的基础设施(主要是多线程.锁.条件变量)来阐述一个经典问题——生产者消费者模型,并给出 ...
随机推荐
- Android 基于Netty的消息推送方案之对象的传递(四)
在上一篇文章中<Android 基于Netty的消息推送方案之字符串的接收和发送(三)>我们介绍了Netty的字符串传递,我们知道了Netty的消息传递都是基于流,通过ChannelBuf ...
- 根据id查询数据(向前台返回json格式的数据)
/** *@description 根据主键查询Bean */ @RequestMapping(value="/getBean/{getId}") public void getB ...
- Request.ServerVariables 服务器环境变量
Request.ServerVariables["Url"] 返回服务器地址 Request.ServerVariables["Path_Info"] 客户端提 ...
- ZOJ3549 Little Keng(快速幂)
转载请注明出处: http://www.cnblogs.com/fraud/ ——by fraud Little Keng Time Limit: 2 Seconds Me ...
- 关于本地$.get(url,function(data)),异步获取数据
起初 此处url为本地同目录下的html片段 $.get(url,function(data) { alert(url); $("#gallery").append(data); ...
- 文成小盆友python-num5 -装饰器回顾,模块,字符串格式化
一.装饰器回顾与补充 单层装饰器: 如上篇文章所讲单层装饰器指一个函数用一个装饰器来装饰,即在函数执行前或者执行后用于添加相应的操作(如判断某个条件是否满足). 具体请见如下: 单层装饰器 双层装饰器 ...
- flask开发restful api系列(5)-短信验证码
我们现在开发app,注册用户的时候,不再像web一样,发送到个人邮箱了,毕竟个人邮箱在移动端填写验证都很麻烦,一般都采用短信验证码的方式.今天我们就讲讲这方面的内容. 首先,先找一个平台吧.我们公司找 ...
- cf D. Queue
http://codeforces.com/contest/353/problem/D #include <cstdio> #include <cstring> #includ ...
- 【Xamarin开发IOS-IOS生命周期】
iOS的应用程序的生命周期,还有程序是运行在前台还是后台,应用程序各个状态的变换,这些对于开发者来说都是很重要的. iOS系统的资源是有限的,应用程序在前台和在后台的状态是不一样的.在后台时,程序会受 ...
- [置顶] css3 befor after 简单使用 制作时尚焦点图相框
:befor.:after是CSS的伪元素,什么是伪元素呢?伪元素用于向某些选择器设置特殊效果. 我们用CSS手册可以查询到其基本的用法: E:before/E::before 设置在对象前(依据对象 ...