线程管理基础

C++11 所有的线程都封装在<thread>头文件中,使用命名空间std说明。

最简单的例子:

#include <iostream>
#include <thread>void hello() {std::cout << "hello world !\n";
}int main() {std::thread t(hello);  // 定义一个线程,注意是直接构造执行的t.join();              // 在这里进行等待return 0;
}

每个线程都必须有一个初始函数,新线程在函数中执行。join()的作用是等待线程执行完毕,然后向下执行。该例子中,如果没有join,那么可能出现主线程执行完毕,子线程还未完成,出现异常。

C++线程库启动线程可以归结为构造一个std::thread对象,使用可调用类型进行构造。传入带有函数调用符类型的参数实例,可以用来替代默认的构造函数。

joindetach的区别:

  • join是用于等待线程执行完毕后,再向下执行
  • detach是声明是线程再后台执行,不必等待线程执行完毕,主线程可以继续向下执行。而且此时不能有其他的std::thread对象能引用它。
  • 两者都不能对没有执行的线程进行操作,保险的做法是先进行joinable的判断,然后调用函数。

线程使用的注意事项:尽量不要在线程中访问局部变量的引用或者指针,保险的做法是复制数据到线程中。
线程本身不能返回数据,因此需要使用引用的方式。。

以下是构造线程的一般方法:

#include <iostream>
#include <thread>
#include <string>
#include <algorithm>// 最普通方式
void do_some_work() {std::cout << "do some work !\n";
}// 函数对象方式
class back_ground_task {public:void operator()()const {do_something();do_something_else();}void do_something()const {std::cout << "do something !\n";}void do_something_else()const {std::cout << "do something else !\n";}
};// 析构方保护线程
class thread_guard {public:explicit thread_guard(std::thread& t_): t(t_) {}~thread_guard() {if(t.joinable()) {t.join();}}thread_guard(thread_guard const&) = delete;thread_guard& operator=(thread_guard const&) = delete;private:std::thread& t;
};// 参数测试
void arg_test(int i, const std::string& str) {std::cout << "i=" << i << ", string=" << str << std::endl;
}// 类成员函数测试
class X {public:void func(int i) {std::cout << "X func i=" << i << std::endl;}
};// 引用方式传递参数
void ref_func_test(std::string& str) {str += str;
}void do_work(int i) {std::cout << "create thread: " << i << std::endl;
}void create_epoch_threads(int n) {std::vector<std::thread>threads;// 量产线程for(unsigned j = 0; j < n; ++j) {threads.emplace_back(std::thread(do_work, j));}// 对每个线程进行join操作std::for_each(threads.begin(), threads.end(),std::mem_fn(&std::thread::join));
}int main() {// 最基本方式std::thread t(do_some_work);t.join();// 函数对象的方式back_ground_task f;std::thread t1(f);t1.join();// 使用类构造的方式std::thread t2(do_some_work);thread_guard g(t2);// 匹配参数std::thread t3(arg_test, 2, "arg test");t3.join();// 引用传递std::string str("test_ref_");std::thread t4(ref_func_test, std::ref(str));  // 注意std::reft4.join();std::cout << str << std::endl;// 类成员函数X my_x;std::thread t5(&X::func, &my_x, 10);t5.join();// 批量生产线程create_epoch_threads(3);return 0;
}

转移线程所有权

线程没有拷贝操作!!! ,所有权的转移都是通过std::move实现的。这种类型的资源是不能直接赋值的,没有意义,只能直接创建。

线程的所有权可以在函数外进行转移。

std::thread f() {void some_function();return std::thread(some_function);
}std::thread g() {void some_other_function(int);std::thread t(some_other_function, 42);return t;
}

编译器有ROV,不用担心出现多余的拷贝,这里返回值就是相当于移动操作。

所有权可以在函数内部传递,同样的也可以作为函数的参数,不过要使用std::move进行操作。

void f(std::thread t);
void g() {void some_function();f(std: thread(some_function));   // 可以直接在参数处进行构造std::thread t(some_function);    // 直接进行移动操作f(std::move(t));
}

可以使用类构造的方式,直接传入线程,而不是各个参数。这样,可以更加简化操作。

#include <iostream>
#include <thread>void do_something(int i) {/*do some thing here*/for(int j = 0; j < 10; ++j) {i = j;}
}struct func {int& i;func(int& i_): i(i_) {}void operator()() {for(unsigned j = 0; j < 1000000; ++j) {do_something(i);}}
};class scoped_thread {public:explicit scoped_thread(std::thread t_): t(std::move(t_)) {if(!t.joinable()) {throw std::logic_error("No thread");}}~scoped_thread() {t.join();}scoped_thread(scoped_thread const&) = delete;scoped_thread& operator=(scoped_thread const&) = delete;private:std::thread t;
};void f() {int some_local_state;scoped_thread t(std::thread(func(some_local_state)));// do something in current thread
}int main() {f();
}

运行时线程的数量

std::thread::hardware_concurrency()函数返回CPU核心的数量或者是一个程序中最多能同时并发的函数数量。

给出一个多线程版本的std::accumulate函数。

#include <algorithm>
#include <thread>
#include <iostream>
#include <vector>
#include <ctime>template <typename Iterator, typename T>
struct accumulate_block {void operator()(Iterator first, Iterator last, T& result) {result = std::accumulate(first, last, result);}
};template<typename Iterator, typename T>
T parallel_accumulate(Iterator first, Iterator last, T init) {unsigned long const length = std::distance(first, last);if(length <= 0)return init;// 每个线程最少的任务数量和可能线程最多数量unsigned long const min_per_thread = 25;unsigned long const max_threads =(length + min_per_thread - 1) / min_per_thread;unsigned long const hardware_threads = std::thread::hardware_concurrency();unsigned long const num_threads =std::min(hardware_threads != 0 ? hardware_threads : 2, max_threads);// 每个线程分配的任务unsigned long const block_size = length / num_threads;// 存放结果std::vector<T>results(num_threads);// 存放线程,注意不要放置主线程std::vector<std::thread>threads(num_threads - 1);// 并行化计算Iterator block_start = first;for(unsigned long i = 0; i < (num_threads - 1); ++i) {Iterator block_end = block_start;std::advance(block_end, block_size);threads[i] = std::thread(accumulate_block<Iterator, T>(),block_start, block_end, std::ref(results[i]));block_start = block_end;}// 计算可能的剩余量,并等待所有线程结束accumulate_block<Iterator, T>()(block_start, last, results[num_threads - 1]);std::for_each(threads.begin(), threads.end(),std::mem_fn(&std::thread::join));// 每个线程计算的结果进行汇总return std::accumulate(results.begin(), results.end(), init);
}int main() {using LL = unsigned long long;LL N = 200000000;LL init = 0;std::vector<LL>vec(N);for(LL i = 0; i < N; ++i) {vec[i] = 1;}using Iter = std::vector<LL>::iterator;// 并行化计算auto start = clock();std::cout << parallel_accumulate<Iter, int>(vec.begin(), vec.end(), init) << std::endl;auto stop = clock();std::cout << "time=" << stop - start << std::endl;// 串行计算start = clock();std::cout << std::accumulate(vec.begin(), vec.end(), init) << std::endl;stop = clock();std::cout << "time=" << stop - start << std::endl;return 0;
}
/*
输出结果:
200000000
time=469
200000000
time=1924运行环境:Intel I7 4710MQ,四核
时间差不多是4倍
*/

补充:std::accumulate()是累加函数,std::advance()是迭代器移动函数,必须是前向或者双向迭代器才可以。

注意:不能从线程中直接返回一个值,在这里采取引用的方式进行解决。

识别线程

C++中,线程的标识符类型是std::thread::id。使用std::threadget_id()成员直接获取。一般来说,这种方式是用来对线程进行检测的,以判断是否需要进行有关的操作。

C++11 多线程 线程管理基础相关推荐

  1. [转]C++ 11 多线程--线程管理

    转载地址:https://www.cnblogs.com/wangguchangqing/p/6134635.html 说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并 ...

  2. C++ 11 多线程--线程管理

    说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并行是指两个或多个独立的操作同时进行.注意这里是同时进行,区别于并发,在一个时间段内执行多个操作.在单核时代,多个线程是并 ...

  3. C++11多线程----线程管理

    说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并行是指两个或多个独立的操作同时进行.注意这里是同时进行,区别于并发,在一个时间段内执行多个操作.在单核时代,多个线程是并 ...

  4. C++11线程管理基础

    1. 启动线程 在C++ 11中线程是在std::thread对象创建时启动.因为我们把启动线程的重心放在如何构造这个thread对象,其构造函数有以下几个: //仅仅是构造一个线程类,但没有和具体化 ...

  5. C#中的多线程-线程同步基础 (控制线程数量)

    同步要领 下面的表格列展了.NET对协调或同步线程动作的可用的工具: 简易阻止方法 构成 目的 Sleep 阻止给定的时间周期 Join 等待另一个线程完成 锁系统 构成 目的 跨进程? 速度 loc ...

  6. c++11の简单线程管理

    1.简单的例子 #include "stdafx.h" #include <iostream> #include <thread>void function ...

  7. C++11 多线程线程共享数据

    共享数据的问题 这些在操作系统中都有详细的介绍,可以回顾操作系统课程..很典型的就是数据竞争问题. 互斥量保护数据 最原始的方式:使用std::mutex创建互斥量,使用成员lock()加锁,使用成员 ...

  8. C++多线程并发中线程管理

    一.何为并发 刚开始接触计算机编程语言时,我们编写一个程序,在main入口函数中调用其它的函数,计算机按我们设定的调用逻辑来执行指令获得结果.如果我们想在程序中完成多个任务,可以将每个任务实现为一个函 ...

  9. 线程学习基础(1):单线程爬虫和多线程爬虫的效率比照

    线程学习基础:单线程爬虫和多线程爬虫的效率比照 1. 并发线程的需求 2. 线程提速方法 3. 如何选择并发编程的三种方式 3.1 什么是CPU密集型计算.IO密集型计算? 3.1.1 CPU密集型( ...

最新文章

  1. databinding 入门 知识 给TextView 赋值
  2. 用java代码实现Singleton,为什么在Java代码中实现Singleton模式(有时被认为是Java世界中的反模式)?...
  3. easyDarwin--开源流媒体实现
  4. sqlserver 2012 查询时提示“目录名称无效”
  5. CentOS7系统上Kubernetes集群搭建
  6. unity vr 交互_基于手动的VR / MR交互,用于删除实体
  7. 计算机u打字,win7电脑打字打不出来怎么办
  8. 《迷人的8051单片机》---3.2 语句
  9. java获取数组穷举_被BAT疯抢的Java工程师,都是怎么拿到年薪50W的offer
  10. 静态成员内部类和非静态成员内部类的实例化方式
  11. linux没有.brashrc文件,Linux 安装 Redis4.0.6
  12. ids和ips主要区别在于_数控机床和普通机床的最主要的区别是在于什么,你知道吗?...
  13. (ORBSLAM3关联文章)论文翻译Inertial-Only Optimization for Visual-Inertial Initialization视觉惯性初始化的仅惯性优化
  14. 拓端tecdat|R语言深度学习:用keras神经网络回归模型预测时间序列数据
  15. 禅道类似软件_推荐几款不错的项目管理软件
  16. 移动领先100笔试面试经验
  17. 使用usb to ttl串口下载器破解移动电视盒子(CM201-2)
  18. GPRS远程开关 2 AIR202模块
  19. 主要搜索引擎(Google和百度、雅虎)的站内搜索代码
  20. Python爬虫是什么?

热门文章

  1. string的operate+=
  2. 算法优化——位运算的优化技巧
  3. Python中中文字符也算单个字符
  4. WARNING:tensorflow:Entity <bound method GRUCell.call of <tensorflow.python.ops.rnn_cell_impl.GRUCell
  5. HanLP自定义词典注意事项
  6. Spring框架中集合属性为对象的注入方法
  7. 分享免费的2.4G板载PCB天线封装(AD)、WIFI天线、GSM天线、433M天线
  8. 【QCustomPlot】1.3 - 运行官方例程Demo,介绍功能
  9. CleanCodeHandbook Chapter 9: Binary Search(48-50)
  10. 【Java】内存解析