1. C++多线程库
传统的C++(C++11之前)中并没有引入线程这个概念,在C++11出来之前,如果我们想要在C++中实现多线程,需要借助操作系统平台提供的API,比如Linux的<pthread.h>,或者windows下的<windows.h>
C++11提供了语言层面上的多线程,包含在头文件中。它解决了跨平台的问题,提供了管理线程、保护共享数据、线程间同步操作、原子操作等类。C++11 新标准中引入了5个头文件来支持多线程编程,如下图所示:
2、如何使用C++线程库创建多线程
2.1、普通函数充当线程处理函数创建线程
// 1、普通函数充当线程处理函数创建线程
void print() {
cout << "线程启动函数" << endl;
}
void test1() {
thread t1(print); //创建线程
if (t1.joinable()) { //判断是否可以被加入
t1.join();
}
}
/*
并发实现
1、多个进程实现并发
1.1 主要解决的是进程间通信问题
1.1.1 同一台电脑上,主要通过管道、文件、消息队列、内存共享实现
1.1.2 不同电脑主要网络编程实现
2、单进程多线程实现并发
2.1 一个进程中所有线程共享内存内存空间的形式
2.2 资源竞争
C++中的多线程
#include<thread>
创建线程,线程处理函数
创建线程未做任何处理,调用abort终止我们的程序
join();加入\汇合线程,阻塞主线程
detach();分离,驻留后台
对于同一线程,join(),detach()只能调用一次
joinable() 判断当前线程是否可以被join或detach,如果可以返回true,否则返回false
*/
2.2、采用Lambda表达式充当线程处理函数
// 2、采用Lambda表达式充当线程处理函数
void test2() {
thread t1([]() {cout << "Lambda式充当线程处理函数" << endl; }); //创建线程
if (t1.joinable()) {
t1.join();
}
}
2.3、带参数创建线程
- 1、普通参数
// 普通参数
void printData(int num) {
cout << "id:" << num << endl;
}
void test3() {
int num = 1;
thread t1(printData, num); //拷贝
if (t1.joinable()) {
t1.join();
}
}
- 2、传引用参数
//传引用
void printReference(int& num) {
num = 1001;
cout << "子线程" << num << endl;
}
void test4() {
int num = 1;
thread t1(printReference, ref(num)); //ref:包装引用作为传递的值
if (t1.joinable()) {
t1.join();
}
cout << "主线程" << num << endl;
}
- 3、传智能指针
//传智能指针
void printPtr(unique_ptr<int> ptr) {
cout << "智能指针的值:" << *ptr.get() << endl; //100
}
void test5() {
unique_ptr<int> ptr(new int(100));
thread t1(printPtr,move(ptr)); //move:对象转移
if (t1.joinable()) {
t1.join();
}
cout << "主线程" << ptr.get() <<endl; //此处为0,因为ptr对象已经转移
}
2.4、带参数创建线程
- 1、仿函数形式:类名的方式调用
// 仿函数形式:类名的方式调用
class Function {
public:
void operator()() {
cout << "重载()" << endl;
}
};
void test6() {
//对象
Function object;
thread t1(object);
if (t1.joinable()) {
t1.join();
}
//匿名对象
thread t2((Function())); //此处需要注意括号的数量
if (t2.joinable()) {
t2.join();
}
}
- 2、普通类中的成员函数
//普通类中的成员函数
class MM {
public:
void print(int& id) {
cout << "id:" << id << endl;
}
};
void test7() {
MM mm;
int id = 1;
thread t1(&MM::print, mm, ref(id)); //第一个是类的成员函数指针,第二个参数指明函数指针属于那个对象,第三个是参数
if (t1.joinable()) {
t1.join();
}
}
创建线程的完整的测试代码
#include<iostream>
#include<thread>
using namespace std;
// 1、普通函数充当线程处理函数创建线程
void print() {
cout << "线程启动函数" << endl;
}
void test1() {
thread t1(print);
if (t1.joinable()) {
t1.join();
}
}
// 2、采用Lambda表达式充当线程处理函数
void test2() {
thread t1([]() {cout << "Lambda式充当线程处理函数" << endl; }); //创建线程
if (t1.joinable()) {
t1.join();
}
}
// 3、带参数创建线程
// 3.1 普通参数
void printData(int num) {
cout << "id:" << num << endl;
}
void test3() {
int num = 1;
thread t1(printData, num);
if (t1.joinable()) {
t1.join();
}
}
//3.2传引用
void printReference(int& num) {
num = 1001;
cout << "子线程" << num << endl;
}
void test4() {
int num = 1;
thread t1(printReference, ref(num)); //ref:包装引用作为传递的值
if (t1.joinable()) {
t1.join();
}
cout << "主线程" << num << endl;
}
//3.3传智能指针
void printPtr(unique_ptr<int> ptr) {
cout << "智能指针的值:" << *ptr.get() << endl; //100
}
void test5() {
unique_ptr<int> ptr(new int(100));
thread t1(printPtr,move(ptr));
if (t1.joinable()) {
t1.join();
}
cout << "主线程" << ptr.get() <<endl; //此处为0,因为ptr对象已经转移
}
// 4、通过类中成员函数去创建
// 4.1仿函数形式:类名的方式调用
class Function {
public:
void operator()() {
cout << "重载()" << endl;
}
};
void test6() {
//对象
Function object;
thread t1(object);
if (t1.joinable()) {
t1.join();
}
//匿名对象
thread t2((Function())); //此处需要注意括号的数量
if (t2.joinable()) {
t2.join();
}
}
// 4.2普通类中的成员函数
class MM {
public:
void print(int& id) {
cout << "id:" << id << endl;
}
};
void test7() {
MM mm;
int id = 1;
thread t1(&MM::print, mm, ref(id)); //第一个是类的成员函数指针,第二个参数指明函数指针属于那个对象,第三个是参数
if (t1.joinable()) {
t1.join();
}
}
int main() {
test1();
test2();
test3();
test4();
test5();
test6();
test7();
return 0;
}
3、4个对线程操作的函数
1、get_id() 获取线程id
//No.1 获取线程id
void threadFunc()
{
cout << "id:" << this_thread::get_id() << endl;
using namespace this_thread;
cout << "id:" << get_id() << endl;
}
2、sleep_for() 延时函数
//No.2 sleep_for 延时函数
void threadSleep_for()
{
cout << "Sleep_for_id:" << this_thread::get_id() << "启动" << endl;
// this_thread::sleep_for(2s);
this_thread::sleep_for(chrono::seconds(3)); //让线程延迟3秒执行
cout << "Sleep_for_id:" << this_thread::get_id() << "结束" << endl;
}
3、yield() 让线程放弃执行
//No.3 yield 让线程放弃执行 ,让操作系统调用另一个线程执行
void threadYield(chrono::microseconds duration)
{
cout << "Yield id:" << this_thread::get_id() << "启动" << endl;
auto startTime = chrono::high_resolution_clock::now();
auto endTime = startTime + duration;
do
{
this_thread::yield();
cout << 1 << endl;
} while (chrono::high_resolution_clock::now() < endTime);
cout << "Yield id:" << this_thread::get_id() << "结束" << endl;
}
- 在C++11标准中,
std::this_thread::yield()
函数可以用来提示调度器当前线程愿意让出CPU资源,使得其他线程或进程可以被调度执行。具体来说,yield()
函数会使得当前线程从运行状态变为就绪状态,让出CPU,以便其他线程可以继续执行。- 然而,需要注意的是,
yield()
函数并不会导致当前线程被完全阻塞,即它并不能保证当前线程会被暂停一段时间,也不能保证其他线程会立即被调度执行。调度器可以选择忽略yield()
的提示,而继续执行当前线程,或者切换到其他线程。- 因此,
yield()
函数的主要作用是提示调度器当前线程可以被调度器抢占,以尽量减小系统的负载,避免忙等待和饥饿现象,而不是堵塞当前线程。如果需要在特定的时间段内堵塞当前线程,可以使用其他函数,
例如std::this_thread::sleep_for()
和std::this_thread::sleep_until()
函数。
4、sleep_until() 阻塞线程,直到sleep_time溢出
//No.4 阻塞线程,直到sleep_time溢出
void threadSleep_until() {
time_t t = chrono::system_clock::to_time_t(chrono::system_clock::now()); // 返回当前的时间点,使用的是系统时钟
cout << "999:" << t << endl;
tm* ptm = new tm; //创建一个tm结构体指针ptm,用于存储时间的分解信息
localtime_s(ptm, &t); //使用localtime_s()函数将时间戳转换为本地时间,并将结果保存在ptm指向的结构体中。
if (ptm != nullptr) { //确保时间转换成功。
this_thread::sleep_until(chrono::system_clock::from_time_t(mktime(ptm))); // 使用chrono::system_clock::from_time_t()函数将ptm中的时间信息转换为chrono::system_clock的时间点类型
}
cout << put_time(ptm, "%X"); //最后,使用put_time()函数将ptm中的时间信息以指定的格式输出。
}
完整的测试代码
#include<iostream>
#include<chrono>
#include<thread>
#include<iomanip>
using namespace std;
//No.1 获取线程id
void threadFunc()
{
cout << "子线程id:" << this_thread::get_id() << endl;
using namespace this_thread; //使用命名空间 get_id()属于this_thread命名空间下的
cout << "子线程id:" << get_id() << endl;
}
// No.2 sleep_for() 延时函数
void threadSleep_for() {
cout << "Sleep_for id:" << this_thread::get_id()<<"启动成功"<<endl;
// this_thread::sleep_for(2s); //延时2s执行
this_thread::sleep_for(chrono::seconds(2)); //延时2s执行,和上面一样的效果
cout << "Sleep_for id:" << this_thread::get_id() << "线程结束" << endl;
}
//No.3 yield() 让线程放弃执行,让操作系统调度另一个线程执行
void threadYield(chrono::microseconds duration) {
cout << "Yield id:" << this_thread::get_id() << "启动" << endl;
auto startTime = chrono::high_resolution_clock::now();
auto endTime = startTime + duration;
do {
this_thread::yield();
cout << 1 << endl;
} while (chrono::high_resolution_clock::now() < endTime);
cout << "Yield id:" << this_thread::get_id() << "结束" << endl;
}
//No.4 阻塞线程,直到sleep_time溢出
void threadSleep_until() {
time_t t = chrono::system_clock::to_time_t(chrono::system_clock::now()); // 返回当前的时间点,使用的是系统时钟
cout << "999:" << t << endl;
tm* ptm = new tm; //创建一个tm结构体指针ptm,用于存储时间的分解信息
localtime_s(ptm, &t); //使用localtime_s()函数将时间戳转换为本地时间,并将结果保存在ptm指向的结构体中。
if (ptm != nullptr) { //确保时间转换成功。
this_thread::sleep_until(chrono::system_clock::from_time_t(mktime(ptm))); // 使用chrono::system_clock::from_time_t()函数将ptm中的时间信息转换为chrono::system_clock的时间点类型
}
cout << put_time(ptm, "%X"); //最后,使用put_time()函数将ptm中的时间信息以指定的格式输出。
}
int main() {
cout << "主线程id:" << this_thread::get_id() << endl;
thread t3(threadYield, chrono::microseconds(100));
t3.detach();
thread t1(threadFunc);
t1.join();
thread t2(threadSleep_for);
t2.join();
threadSleep_until();
cout << 1111 << endl;
return 0;
}
4、带返回值的线程处理函数
1、带返回值的普通函数充当线程处理函数
//No.1 带返回值的普通函数充当线程处理函数
int testThreadOne()
{
return 1001;
}
2、带返回值的类的成员函数充当线程处理函数
//No.1 带返回值的普通函数充当线程处理函数
//No.2 带返回值的类的成员函数充当线程处理函数
class MM
{
public:
int mmThread(int num)
{
cout << "MMthread_id:" << this_thread::get_id() << endl;
num *= 2;
chrono::milliseconds duration(5000);
this_thread::sleep_for(duration); //使当前线程休眠 5 秒,即暂停执行
return num;
}
};
void testAsync() {
future<int> result = async(testThreadOne);
cout << result.get() << endl;
MM mm;
auto temp = async(&MM::mmThread, &mm, 1999); //async(&MM::mmThread, mm, 1999) 在使用类成员函数作为异步任务时,应该将类实例mm作为引用或指针传递给async函数。
cout << temp.get() << endl;
//async的其他参数
//launch::async : 创建线程,执行线程处理函数(默认是这种方式)
//launch::deferred : 线程处理函数延迟到 我们调用wait()和get()方法时候才会执行,本质是没有创建子线程↓
auto testDerred = async(launch::deferred, &MM::mmThread, &mm, 1998);
cout<<testDerred.get(); //只有调用get(),才会创建线程
auto testAsyncParam = async(launch::async, &MM::mmThread, &mm, 100); //直接创建线程
}
3、用thread处理带返回值的线程处理函数
//No.3 用thread处理带返回值的线程处理函数
//1.通过类模板 packaged_task 包装线程处理函数
//2.通过packaged_task的对象调用get_future获取future对象,再通过get()方法得到子线程处理函数的返回值
void testPackage_task() {
//No.1打包普通函数
packaged_task<int(void)> taskOne(testThreadOne); //int(void) int:函数返回值类型,(void):函数参数类型
thread testOne(ref(taskOne));
testOne.join();
cout << taskOne.get_future().get() << endl;
//No.2 打包类的成员函数
MM mm;
packaged_task<int(int)> taskTwo(bind(&MM::mmThread, &mm, placeholders::_1)); //注意这里使用占位符
thread testTwo(ref(taskTwo),20);
testTwo.join();
cout << taskTwo.get_future().get() << endl;
//No.3 打包Lambda表达式
packaged_task<int(int)> taskThree([](int num)
{
cout << "thread_id:" << this_thread::get_id() << endl;
num *= 10;
return num;
});
thread testThree(ref(taskThree), 7);
testThree.join();
cout << taskThree.get_future().get() << endl;
}
4、 promise 获取线程处理函数"返回值"
void testPromiseThread(promise<int>& temp, int data)
{
cout << "thread_id" << this_thread::get_id() << endl;
data *= 3;
temp.set_value(data);
}
void testPromise()
{
promise<int> temp;
thread testp(testPromiseThread, ref(temp), 18);
testp.join();
cout << temp.get_future().get() << endl;
}
完整的测试代码
#include<iostream>
#include<thread>
#include<chrono>
#include<future>
using namespace std;
int returnValue() {
return 100;
}
/*
第一步:采用async:启动一个异步任务,(创建线程,并且执行线程处理函数),返回值future对象
第二步:通过future对象中get()方法获取线程函数返回值
*/
//No.1 带返回值的普通函数充当线程处理函数
int testThreadOne()
{
return 1001;
}
//No.2 带返回值的类的成员函数充当线程处理函数
class MM
{
public:
int mmThread(int num)
{
cout << "MMthread_id:" << this_thread::get_id() << endl;
num *= 2;
chrono::milliseconds duration(5000);
this_thread::sleep_for(duration); //使当前线程休眠 5 秒,即暂停执行
return num;
}
};
void testAsync() {
future<int> result = async(testThreadOne);
cout << result.get() << endl;
MM mm;
auto temp = async(&MM::mmThread, &mm, 1999); //async(&MM::mmThread, mm, 1999) 在使用类成员函数作为异步任务时,应该将类实例mm作为引用或指针传递给async函数。
cout << temp.get() << endl;
//async的其他参数
//launch::async : 创建线程,执行线程处理函数(默认是这种方式)
//launch::deferred : 线程处理函数延迟到 我们调用wait()和get()方法时候才会执行,本质是没有创建子线程↓
auto testDerred = async(launch::deferred, &MM::mmThread, &mm, 1998);
cout<<testDerred.get(); //只有调用get(),才会创建线程
auto testAsyncParam = async(launch::async, &MM::mmThread, &mm, 100); //直接创建线程
}
//No.3 用thread处理带返回值的线程处理函数
//1.通过类模板 packaged_task 包装线程处理函数
//2.通过packaged_task的对象调用get_future获取future对象,再通过get()方法得到子线程处理函数的返回值
void testPackage_task() {
//No.1打包普通函数
packaged_task<int(void)> taskOne(testThreadOne); //int(void) int:函数返回值类型,(void):函数参数类型
thread testOne(ref(taskOne));
testOne.join();
cout << taskOne.get_future().get() << endl;
//No.2 打包类的成员函数
MM mm;
packaged_task<int(int)> taskTwo(bind(&MM::mmThread, &mm, placeholders::_1)); //注意这里使用占位符
thread testTwo(ref(taskTwo),20);
testTwo.join();
cout << taskTwo.get_future().get() << endl;
//No.3 打包Lambda表达式
packaged_task<int(int)> taskThree([](int num)
{
cout << "thread_id:" << this_thread::get_id() << endl;
num *= 10;
return num;
});
thread testThree(ref(taskThree), 7);
testThree.join();
cout << taskThree.get_future().get() << endl;
}
//No.4 promise 获取线程处理函数"返回值"
//第一步:通过promise类模板构建对象,通过调用set_value 是存储函数需要返回的值
//第二步:通过get_future获取future对象,再通过get()方法获取线程处理函数中值
void testPromiseThread(promise<int>& temp, int data)
{
cout << "thread_id" << this_thread::get_id() << endl;
data *= 3;
temp.set_value(data);
}
void testPromiseToThread(future<int>& temp)
{
int num = temp.get();
cout << num << endl;
}
void testPromise()
{
promise<int> temp;
thread testp(testPromiseThread, ref(temp), 18);
testp.join();
cout << temp.get_future().get() << endl;
//temp.set_value(888);
promise<int> data;
data.set_value(999);
auto num = data.get_future();
thread test(testPromiseToThread, ref(num));
test.join();
}
int main() {
// thread t1(returnValue); //这样创建线程无法获取线程的返回值
// t1.join();
// future<int> result = async(testThreadOne);
// cout << result.get() << endl;
// testAsync();
// testPackage_task();
testPromise();
return 0;
}
Time:2023.5.19 (周五)
如果上面代码对您有帮助,欢迎点个赞!!!