C++11多线程教学(二)
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多线程教学(二)的更多相关文章
- C++11多线程教学(一)
		
本篇教学代码可在GitHub获得:https://github.com/sol-prog/threads. 在之前的教学中,我展示了一些最新进的C++11语言内容: 1. 正则表达式(http://s ...
 - C++11多线程教学II
		
从我最近发布的C++11线程教学文章里,我们已经知道C++11线程写法与POSIX的pthreads写法相比,更为简洁.只需很少几个简单概念,我们就能搭建相当复杂的处理图片程序,但是我们回避了线程同步 ...
 - 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 多线程入门教程(一)
		
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/10945309.html 最近在找c++服务端开发的实习(大佬们有推荐吗QAQ..),恰好写了一 ...
 - [.net 面向对象程序设计进阶] (17) 多线程(Multithreading)(二) 利用多线程提高程序性能(中)
		
[.net 面向对象程序设计进阶] (17) 多线程(Multithreading)(二) 利用多线程提高程序性能(中) 本节要点: 上节介绍了多线程的基本使用方法和基本应用示例,本节深入介绍.NET ...
 - 【阿里聚安全·安全周刊】阿里双11技术十二讲直播预约|AWS S3配置错误曝光NSA陆军机密文件
		
关键词:阿里双11技术十二讲直播丨雪人计划丨亚马逊AWS S3配置错误丨2018威胁预测丨MacOS漏洞丨智能风控平台MTEE3丨黑客窃取<权利的游戏>剧本|Android 8.1 本 ...
 - Java多线程(二)关于多线程的CPU密集型和IO密集型这件事
		
点我跳过黑哥的卑鄙广告行为,进入正文. Java多线程系列更新中~ 正式篇: Java多线程(一) 什么是线程 Java多线程(二)关于多线程的CPU密集型和IO密集型这件事 Java多线程(三)如何 ...
 
随机推荐
- VS2012生成不依赖运行时不依赖MFC的MFC程序
			
转载请注明来源:http://www.cnblogs.com/xuesongshu/ 1.新建MFC或者Win32工程,全部使用默认设置 2.设置工程属性,展开配置属性,转到:常规~MFC的使用,修改 ...
 - ios 一个app启动另一个app
			
问题描述:需要从一个ios应用程序中,能启动另一个ios应用程序. 开发环境:xcode7.3.1 关键词:白名单(LSApplicationQueriesSchemes).注册自己的URL Demo ...
 - Object类型
			
Object类型 我们看到的大多数引用类型值都是Object类型的实例,虽然Object类型不具备多少功能,但是在储存和传输数据上的确是不错的选择. 2.对象字面量表示法 调用Object构造函数: ...
 - UVa11384
			
题意:给定正整数n,求把1,2,--,n中所有书都变成0的最少操作次数,每次操作可从序列中选择一个或多个整数,同时减去一个相同的正整数. 分析:如1,2,3,4,5,6, 第一步将4,5,6同时减4, ...
 - Windows服务器nginx+tomcat服务负载均衡
			
一.安装两个tomcat服务自启动 1. 解压两个tomcat,名称为分别1,2 2. 配置环境变量 3. 修改文件server.xml中的三个端口号,使得两个tomcat不冲突 (1)<Ser ...
 - c#汉字转为拼音
			
using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressi ...
 - 编写高性能 Web 应用程序的 10 个技巧
			
使用 ASP.NET 编写 Web 应用程序的简单程度令人不敢相信.正因为如此简单,所以很多开发人员就不会花时间来设计其应用程序的结构,以获得更好的性能了.在本文中,我将讲述 10 个用于编写高性能 ...
 - ES6学习笔记(九)
			
1.概述 ES5的对象属性名都是字符串,这容易造成属性名的冲突.比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin模式),新方法的名字就有可能与现有方法产生冲突.如果有一种机制 ...
 - python学习之html从0开始(一)
			
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content ...
 - 去掉iphone手机滑动默认行为
			
/*去掉iphone手机滑动默认行为*/ $('body').on('touchmove', function (event) { event.preventDefault(); });