C++ 多线程的使用
使用多线程可以将需要同时执行的各个函数分配到不同的线程中,从而实现”并行化“处理数据的要求。在处理多线程时有将处理后的数据输出和不确定线程数的需求。本教程针对线程在使用过程中的问题,提出了一些解决方案。
- 新建线程
- 往线程中传入参数
- 不定长度的多线程
- 线程在类中的使用
1. 新建线程
C++11中新建线程十分简单,只需要在头文件中包含thread,然后在需要的位置使用即可。
// 新建多线程
#include<iostream>
#include<thread>
void print()
{
cout << "这个执行多线程的结果!" << endl;
}
int main()
{
thread Thread(print);
Thread.join();
return 0;
}
代码中首先使用初始化了Thread对象,对象中参数是多需要调用的函数名,然后使用join阻塞,只到线程完成运行才进行下一步操作。
线程阻塞有两种方法:join()和detach()。
其中***join()表示程序在当前阻塞直到现成运行完才会执行join()***之后的语句。
***detach()***表示程序不会再当前阻塞,会向下继续执行。
如果不添加***join()或detach()***语句,则程序会报中断错误。其原因是在程序结束时会销毁作用域内使用的变量,当销毁到线程变量时,由于线程在运行,所以无法销毁而报的中断错误。
使用多线程建议使用***join()阻塞线程,因为detach()***可能会出现线程结束时该线程还未运行结束的情况,从而结果出现错误。
2. 往线程中传入参数
第1节中的线程并未传入参数,传入参数其实也很简单,只需要在调用函数是将参数传入即可。
// 线程中传入参数
#include<iostream>
#include<thread>
void thread_print(int i)
{
cout << "普通函数输出的结果为:" << i << endl;
}
int main()
{
thread Thread(thread_print, 6);
Thread.join();
return 0;
}
通过上述代码,可以成功传入参数。
但有些线程存在的意义是处理某些数据后,将处理后的数据返回主程序使用。上述代码中返回值均为void,不存在返回值,而且现成不是串行运算的,无法使用return获得返回值。
解决返回值有两种方法:一种是使用future和promise,这种方法在第5节介绍;另一种是使用引用的方法获得。
// 线程中传入参数
#include<iostream>
#include<thread>
void thread_print(int i, int& sum)
{
sum = (i + i);
cout << "普通函数输出的结果为:" << i << endl;
}
int main()
{
int b;
// std::ref是把一个变量包装成一个"引用",不使用std::ref会报错(“invoke”: 未找到匹配的重载函数)
thread Thread(thread_print, 6, std::ref(b);
Thread.join();
return 0;
}
3. 不定长度的多线程
在进行数据处理或算法迭代时,为提升速度,可以使用多线程的方法将“互不打扰”的函数“并行计算”。但在进行“并行计算”的时候,往往无法知道具体需要迭代的次数。
解决不定长度的线程的方式有两个:使用线程池或使用vector包装thread,这两种方法的基本原理都是使用一个容器包装thread,不同的是线程池可以限定线程的申请数量,而使用vector包装则可能会出现线程申请过多而影响其他程序的情况。使用线程池的方法见博客,本节主要介绍使用vector容器包装线程的方法。
#include<iostream>
#include<thread>
#include<vector>
#include<ctime> // 主要是计时
#include <Windows.h> // 主要是延时
void thread_print(int i)
{
// 使用延时的方法证明多进程
Sleep(i * 1000);
cout << "运行完成!!!" << i << endl;
}
int main()
{
float start, end;
start = clock();
vector<thread> Thread;
for (int i = 0; i < 10; ++i)
{
Thread.push_back(thread(thread_print, i));
}
for (int i = 0; i < 10; ++i)
{
Thread[i].join();
}
end = clock();
cout << "time = " << (end - start) / CLOCKS_PER_SEC << "s" << endl;
return 0;
}
// 运行时间是9.034s
可以看出来,循环10次的运行结果是9.034秒。该方法同样验证了可以使用多线程增快运行速度。
4. 线程在类中的使用
上述的方法基本都是在普通函数中使用的,在面向对象时,有时需要针对类中的函数进行多线程执行。在类中的使用主要包括两种:在在类外实例化对象进行多线程和类内使用类的成员函数构建多线程两种情况。
1. 在类外实例化对象进行多线程
在需要引用类的成员函数时,一般会认为与独立出来的函数类似。即:
class ThreadClass
{
public:
void class_print(int i);
};
int main()
{
vector<thread> Thread;
for (int i = 0; i < 10; ++i)
{
Thread.push_back(thread(&ThreadClass::class_print, i));
}
for (int i = 0; i < 10; ++i)
{
Thread[i].join();
}
return 0;
}
但这样的方式会报错: C2672 “invoke”: 未找到匹配的重载函数。 所以需要再引用试再传入实例化后的成员函数或者将引用的函数声明为static。
class ThreadClass
{
public:
void class_print(int i);
static void class_print(int i); // 这样声明可以像使用普通函数那样构建多线程,但并不实用
};
int main()
{
vector<thread> Thread;
ThreadClass threadclass;
for (int i = 0; i < 10; ++i)
{
Thread.push_back(thread(&ThreadClass::class_print, threadclass, i));
}
for (int i = 0; i < 10; ++i)
{
Thread[i].join();
}
return 0;
}
void ThreadClass::class_print(int i)
{
cout << "类中输出的结果为:" << i << endl;
}
运行结果:
由于使用了多线程,所以在输出时不会严格按照理想的形式输出,输出结果会出现错乱的现象。
2. 类内使用类的成员函数构建多线程
类内使用多线程与在类外使用多线程类似,唯一不同的是类内引用自己的成员函数无法实例化对象,所以需要使用C++支持的this指针。
class ThreadClass
{
public:
void print_thread();
};
void ThreadClass::print_thread()
{
vector<thread> Thread;
for (int i = 0; i < 10; ++i)
{
Thread.push_back(thread(&ThreadClass::class_print, this, i));
}
for (int i = 0; i < 10; ++i)
{
Thread[i].join();
}
}
int main()
{
ThreadClass threadclass;
threadclass.print_thread();
return 0;
}
结果与使用实例化对象构建的多线程类似。
参考博客
C++:创建线程的问题: error C2672: ‘std::invoke’: 找不到匹配的重载函数
/details/118861539)