一、基础知识
- 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程
- 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
- 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。
- 线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果。
- 同步:通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。
二、相关库
C++标准并没有提供对多进程并发的原生支持,所以C++的多进程并发要靠其他API——这需要依赖相关平台。
C++11 标准提供了一个新的线程库,内容包括了管理线程、保护共享数据、线程间的同步操作、低级原子操作等各种类。标准极大地提高了程序的可移植性,以前的多线程依赖于具体的平台,而现在有了统一的接口进行实现。
这里是windows下的c++多线程,Linux下的头文件会有区别。
C++11 新标准中引入了几个头文件来支持多线程编程:
- 1、 < thread > :包含std::thread类以及std::this_thread命名空间。管理线程的函数和类在 中声明.
- 2、 < atomic > :包含std::atomic和std::atomic_flag类,以及一套C风格的原子类型和与C兼容的原子操作的函数。
- 3、 < mutex > :包含了与互斥量相关的类以及其他类型和函数
- 4、 < future > :包含两个Provider类(std::promise和std::package_task)和两个Future类(std::future和std::shared_future)以及相关的类型和函数。
- 5、 < condition_variable > :包含与条件变量相关的类,包括std::condition_variable和std::condition_variable_any。
三、初试多线程
完整示例见github
3.1 线程的创建
用std::thread 创建线程非常的简单,只需要提供线程函数或者函数对象即可,并可以同时指定线程的参数:
#include "stdafx.h"
#include "ThreadLearning.h"
void print_hello()
{
cout << "hello world" << endl;
}
void print(string str)
{
cout << str << endl;
}
void hello_world()
{
string tmp;
//这是不带参数的调用
//创建线程对象t1,绑定线程函数为hello
thread t1(print_hello);
//输出t1的线程ID
std::cout << "ID:" << t1.get_id() << std::endl;
//等待t1线程函数执行结束
//join函数将会阻塞线程,直到线程函数执行结束,如果线程函数有返回值,返回值将被忽略.
t1.join();
//带参数的调用
//创建线程对象ta,绑定线程函数为print
tmp = "A";
thread ta(print, tmp);
ta.join();
tmp = "B";
thread tb(print, tmp);
tb.join();
tmp = "C";
thread tc(print, tmp);
tc.join();
}
int main()
{
hello_world();
return 0;
}
线程函数将会运行于线程对象t中,join函数将会阻塞线程,直到线程函数执行结束,如果线程函数有返回值,返回值将被忽略.
detach可以将线程与线程对象分离,让线程作为后台线程执行,当前线程也不会阻塞了.但是detach之后就无法在和线程发生联系了.如果线程执行函数使用了临时变量可能会出现问,线程调用了detach在后台运行,临时变量可能已经销毁,那么线程会访问已经被销毁的变量.
虽然这种方式创建线程很方便,但是std::thread 出了作用域后将会析构,这个时候线程函数还没执行完则会发生错误
3.2 线程移动
线程不可以复制但是可以移动.但是线程移动后,线程对象将不再代表任何线程了
void thread_move()
{
std::thread t(func, 2, 3, 4);
//移动后,线程对象t不在代表任何线程
std::thread t1(std::move(t));
//t.join();
t1.join();
}
move后,t的值如下所示:
t = {_Thr={_Hnd=0x00000000 _Id=0 } }
如果继续调用t.join(),会引发异常,因为t不代表任何线程
参考文献
1、多线程编程基础知识
2、C++11多线程基本使用
3、C++ 11 Threads: Make your (multitasking) life easier.
4、C++11 线程:让你的多线程任务更轻松