C++11 Thread线程库的使用
传统的C++(C++11标准之前)中并没有引入线程这个概念,在C++11出来之前,如果我们想要在C++中实现多线程,需要借助操作系统平台提供的API,比如Linux的,或者windows下的 。
本文详细介绍C++11 线程库的基本使用,包括如何创建线程、启动线程、等待线程完成、如何分离线程。
多线程理解视频动画
文章目录
- C++11 Thread线程库的使用
- 1.线程的概念及使用
- 1.1 如果是在单线程下,启动线程后,主程序不会等待子线程执行完函数。想让主程序等待子线程执行完毕。这里使用join()函数
- 1.2 给线程函数传递参数
- 1.3 分离线程detach(),主线程执行完毕,子线程在后台持续运行
- 1.4 判断线程是否可以调用join()或者detach()
- 1.5 判断join()其实是个阻塞函数
- 2.线程函数中的数据未定义错误
- 2.1传递临时变量的问题
- 2.1.1 传引用
- 2.1.2 传指针
- 2.2 传递指针或引用指向局部变量的问题:
- 2.3 传递指针或引用指向已释放的内存的问题
- 2.4 类成员函数作为入口函数,类对象被提前释放
- 2.5 入口函数为类的私有成员函数
1.线程的概念及使用
线程:进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。
进程:进程就是运行中的程序
- 线程内核对象。操作系统用它来管理线程,存放线程统计信息。
- 线程堆栈,用于维护线程在执行代码时,需要的所有函数参数和局部变量。
- 线程的最大数量取决于于CPU的核心数
线程安全:不论运行多少次,如果多线程程序每一次运行的结果都跟单线程运行的结果是始终如一的,那么表名你的线程是安全的。
线程函数:默认情况下我们所写的代码都是只有一个线程的,而这个线程的入口函数是main() 函数, 这是系统默认的。而我们创建的另一个线程也需要一个函数来进入, 这个函数叫做线程函数。
示例1:
//多线程好处//任务分解:耗时的操作,任务分解,实时响应
//数据分解:充分利用多核CPU处理数据
//数据流分解:读写分离,解耦合设计//linux lpthread//要创建线程,我们需要一个可调用的函数或函数对象,作为线程的入口点。
//在C++11中我们可以使用函数指针、函数对象或lambda表达式来实现。创建线程的基本语法如下!//C++11 Thread线程库的基本使用
//创建线程 启动线程 等待线程 完成线程分离
//要创建线程,我们需要一个可调用的函数或函数对象,作为线程的入口点#include<iostream>
#include<thread>
void printHelloWorld()
{std::cout << "Hello World!" << std::endl;}int main()
{//1.创建线程std::thread thread1(printHelloWorld);return 0;
}
如果是在单线程下,启动线程后,主程序不会等待子线程执行完函数。所以会报错。
1.1 如果是在单线程下,启动线程后,主程序不会等待子线程执行完函数。想让主程序等待子线程执行完毕。这里使用join()函数
#include<iostream>
#include <thread>
void printHelloWorld()
{std::cout << "Hello World!" << std::endl;}int main()
{//1.创建线程std::thread thread1(printHelloWorld);thread1.join();return 0;
}
1.2 给线程函数传递参数
#include<iostream>
#include <thread>
#include<string>
void printHelloWorld(std::string msg)
{//std::cout << "Hello World!" << std::endl;std::cout << msg << std::endl;
}int main()
{//1.创建线程std::thread thread1(printHelloWorld, "Hello World!");thread1.join();return 0;
}
1.3 分离线程detach(),主线程执行完毕,子线程在后台持续运行
#include <iostream>
#include <thread>
#include <string>
void printHelloWorld(std::string msg)
{//std::cout << "Hello World!" << std::endl;std::cout << msg << std::endl;
}int main()
{//1.创建线程std::thread thread1(printHelloWorld, "Hello World!");//thread1.join();//等待线程执行完毕后再继续往下执行thread1.detach();//分离线程,主线程执行完毕,子线程在后台持续运行return 0;
}
1.4 判断线程是否可以调用join()或者detach()
#include <iostream>
#include <thread>
#include <string>
void printHelloWorld(std::string msg)
{//std::cout << "Hello World!" << std::endl;std::cout << msg << std::endl;
}int main()
{//1.创建线程std::thread thread1(printHelloWorld, "Hello World!");//thread1.join();//等待线程执行完毕后再继续往下执行//thread1.detach();//分离线程,主线程执行完毕,子线程在后台持续运行bool idJoin = thread1.joinable();if (idJoin){thread1.join();//等待线程执行完毕后再继续往下执行}return 0;
}
如果说我们对一个不能够调用join()或者detach()的线程进行强行调用,程序报错systerm_error
1.5 判断join()其实是个阻塞函数
#include <iostream>
#include <thread>
#include <string>
void printHelloWorld(std::string msg)
{//std::cout << "Hello World!" << std::endl;//std::cout << msg << std::endl;for (int i = 0; i < 10000; i++)std::cout << i << std::endl;
}int main()
{//1.创建线程std::thread thread1(printHelloWorld, "Hello World!");//thread1.join();//等待线程执行完毕后再继续往下执行//thread1.detach();//分离线程,主线程执行完毕,子线程在后台持续运行bool idJoin = thread1.joinable();if (idJoin){thread1.join();//等待线程执行完毕后再继续往下执行}std::cout << "over" << std::endl;return 0;
}
2.线程函数中的数据未定义错误
2.1传递临时变量的问题
2.1.1 传引用
#include<iostream>
#include<thread>void foo(int &x)
{x = x + 1;}
int main()
{std::thread t(foo, 1);//传递临时变量t.join();return 0;}
修改为
#include<iostream>
#include<thread>void foo(int &x)
{x = x + 1;}
int main()
{int a = 1;std::thread t(foo, std::ref(a));//传递临时变量t.join();std::cout << "a=" << a << std::endl;return 0;}
2.1.2 传指针
#include<iostream>
#include<thread>void foo(int *x)
{*x = *x + 100;}
int main()
{int a = 1;std::thread t(foo, &a);//传递临时变量t.join();std::cout << "a=" << a << std::endl;return 0;}
2.2 传递指针或引用指向局部变量的问题:
#include<iostream>
#include<thread>
std::thread t;
int a = 1;
void foo(int *x)
{*x = *x + 1;std::cout << "*x=" << *x << std::endl;
}
void test()
{//int a = 1;t = std::thread(foo, &a);}int main()
{test();t.join();//std::cout << "a=" <<a << std::endl;std::cout << "over" << std::endl;return 0;}
2.3 传递指针或引用指向已释放的内存的问题
#include <iostream>
#include <thread>
void foo(int& x)
{std::cout << x << std::endl; // 访问已经被释放的内存
}
int main()
{int* ptr = new int(1);std::thread t(foo, *ptr); // 传递已经释放的内存delete ptr;t.join();return 0;
}
2.4 类成员函数作为入口函数,类对象被提前释放
#include <iostream>
#include <thread>class MyClass
{
public:void func() {std::cout << "Thread " << std::this_thread::get_id()<< " started" << std::endl;// do some workstd::cout << "Thread " << std::this_thread::get_id()<< " finished" << std::endl;}
};int main()
{MyClass obj;std::thread t(&MyClass::func, &obj);// obj 被提前销毁了,会导致未定义的行为return 0;
}
上面的代码中,在创建线程之后,obj 对象立即被销毁了,这会导致在线程执行时无法访问 obj 对象,可能会导致程序崩溃或者产生未定义的行为。
为了避免这个问题,可以使用 std::shared_ptr 来管理类对象的生命周期,确保在线程执行期间对象不会被销毁。具体来说,可以在创建线程之前,将类对象的指针封装在一个 std::shared_ptr 对象中,并将其作为参数传递给线程。这样,在线程执行期间,即使类对象的所有者释放了其所有权,std::shared_ptr 仍然会保持对象的生命周期,直到线程结束。
以下是使用 std::shared_ptr 修复上面错误的示例:
#include <iostream>
#include <thread>
#include<memory>
class MyClass
{
public:void func() {std::cout << "Thread " << std::this_thread::get_id()<< " started" << std::endl;// do some workstd::cout << "Thread " << std::this_thread::get_id()<< " finished" << std::endl;}
};int main()
{std::shared_ptr<MyClass> obj = std::make_shared<MyClass>();std::thread t(&MyClass::func, obj);t.join();// obj 被提前销毁了,会导致未定义的行为return 0;
}
2.5 入口函数为类的私有成员函数
//5.入口函数为类的私有成员函数#include <iostream>
#include <thread>class MyClass
{
private:friend void myThreadFunc(MyClass* obj);void privateFunc() {std::cout << "Thread "<< std::this_thread::get_id() << " privateFunc" << std::endl;}
};void myThreadFunc(MyClass* obj)
{obj->privateFunc();
}int main() {MyClass obj;std::thread thread_1(myThreadFunc, &obj);thread_1.join();return 0;
}