- #include
- #include
- #include
-
- void sleep() {
- // sleep for 2 seconds
- std::this_thread::sleep_for(std::chrono::seconds(2));
- }
-
- int main() {
- auto startTime = std::chrono::high_resolution_clock::now();
-
- sleep(); // 2s
- sleep(); // 2s
-
- auto stopTime = std::chrono::high_resolution_clock::now();
- auto duration = std::chrono::duration_cast
(stopTime - startTime); -
- std::cout << "Duration: " << duration.count() / 1e6 << "s" << std::endl;
- }
上述代码的运行时长应该是4s左右,这也符合串行编程的一贯逻辑,即执行完一段代码后再执行下一段代码。但是在许多应用中,串行编程会严重影响程序的执行效率,所以从c++11后引入的thread能让我们较快地上手并行编程,也就是多线程编程。
这里先简单介绍一下线程的优势,线程和进程都是操作系统对资源调度的一种抽象,我们可以将线程视作一种轻量级的进程,一个进程中可以包含多个线程,而这些线程共享进程中的全部资源,比如堆栈、代码、全剧数据等等。而线程仅有较少的私有资源,比如线程的调用栈等。因此,我们说线程是一个轻量级的进程。
我们对上面的代码进行修改使其可以多线程地调度:
- // ...
- int main() {
- auto startTime = std::chrono::high_resolution_clock::now();
-
- std::thread t1(sleep);
- std::thread t2(sleep);
-
- t1.join(); // 阻塞主线程,直至线程t1运行结束
- t2.join(); // 阻塞主线程,直至线程t2运行结束
-
- auto stopTime = std::chrono::high_resolution_clock::now();
- auto duration = std::chrono::duration_cast
(stopTime - startTime); -
- std::cout << "Duration: " << duration.count() / 1e6 << "s" << std::endl;
- }
修改后,程序运行时间减少为2s。
函数指针:
- void countdown(int x) {
- while (x --> 0) std::cout << x << std::endl;
- }
-
- int main() {
- std::thread t(countdown, 10); // 使用函数指针创建线程
- }
- Lambda函数:int main() {
- std::thread t([] (int x) {
- while (x --> 0) std::cout << x << std::endl;
- }, 10); // 使用Lambda函数创建线程
- }
函数对象(Functor,Function Object):
- class Base {
- public:
- void operator() (int x) {
- while (x --> 0) std::cout << x << std::endl;
- }
- };
-
- int main() {
- std::thread t(Base(), 10); // 使用函数对象指针创建线程
- }
成员函数:
- class Base {
- public:
- void run(int x) {
- while (x --> 0) std::cout << x << std::endl;
- }
-
- static void static_run(int x) {
- while (x --> 0) std::cout << x << std::endl;
- }
- };
-
- int main() {
- Base b;
- std::thread t1(&Base::run, &b, 10); // 使用成员函数(非静态)指针创建线程,需要传入对象指针(&b)
- std::thread t2(&Base::static_run, 10); // 使用静态成员函数指针创建线程,不需要传入对象指针
- }
thread.join()的含义是等待(类似js中的await)该线程执行结束,但是如果执行两次join则会造成程序终止,因此可以使用thread.joinable()来检查该线程是否可调用join()。
thread.detach()的含义是将子线程从父线程中分离出去。当我们不使用任何thread时,main函数是在一个单进程中的单线程中,当我们创建一个线程时,main成为父线程,而新建的thread成为子线程。与join相同,detach也不能被调用两次,需要用joinable来进行检查。
对于一个thread对象,必须调用join或者detach,否则,当该线程析构时,会终止当前的进程。即当析构时,该thread对象的状态为joinable的话,调用std::terminate;
。c++标准explicit处理的原因是不利于debug,具体原因这里不进行展开。
Mutex是Mutual Exclusion(互斥)的意思,std::mutex的作用是处理所谓的竞争问题(Race Condition),即两个线程同时对一个关键变量(Critical Section,即只能有一个线程或进程同时进行访问的一个或一组变量或状态)进行读写的问题。我们假设有一个变量int x = 0;
,如果连个线程T1和T2同时对变量x进行++操作,则有可能导致两个