C++11 并发编程教程 - Part 1 : thread 初探
2016-08-03 15:50
253 查看
为了能够编译本文的示例代码,你需要有一个支持 C++11 的编译器,笔者使用的是 GCC4.6.1(你需要添加 "-std=c++11" 或 "-std=c++0x" 编译选项以启动 GCC 对 C++11 的支持)[译注:bill 的编译环境为 GCC4.6.3 + codeblocks 10.05 + Ubuntu 12.04,所使用的编译选项为 "-std=gnu++0x"]。
启动线程
启动一个新的线程非常简单,当你创建一个 std::thread 的实例时,它便会自行启动。创建线程实例时,必须提供该线程将要执行的函数,方法之一是传递一个函数指针,让我们以经典的
"Hello world" 来阐释这一方法:
所有的线程工具均置于头文件 <thread> 中。这个例子中值得注意的是对函数 join() 的调用。该调用将导致当前线程等待被 join 的线程结束(在本例中即线程 main 必须等待线程 t1 结束后方可继续执行)。如果你忽略掉对 join() 的调用,其结果是未定义的
—— 程序可能打印出 "Hello from thread" 以及一个换行,或者只打印出 "Hello from thread" 却没有换行,甚至什么都不做,那是因为线程main 可能在线程 t1 结束之前就返回了。
区分线程
每个线程都有唯一的 ID 以便我们加以区分。使用 std::thread 类的 get_id() 便可获取标识对应线程的唯一 ID。我们可以使用 std::this_thread 来获取当前线程的引用。下面的例子将创建一些线程并使它们打印自己的 ID:
依次启动线程并将他们存入 vector 是管理多个线程的常用伎俩,这样你便可以轻松地改变线程的数量。回到正题,就算是上面这样短小简单的例子,也不能断定其输出结果。理论情况是:
Hello from thread 140276650997504
Hello from thread 140276667782912
Hello from thread 140276659390208
Hello from thread 140276642604800
Hello from thread 140276676175616
但实际上(至少在我这里)上述情况并不常见,你很可能得到的是如下结果:
Hello from thread Hello from thread Hello from thread 139810974787328Hello from thread 139810983180032Hello from thread
139810966394624
139810991572736
139810958001920
或者更多其他的结果。这是因为线程之间存在 interleaving 。你没办法控制线程的执行顺序,某个线程可能随时被抢占,又因为输出到 ostream 分几个步骤(首先输出一个 string,然后是 ID,最后输出换行),因此一个线程可能执行了第一个步骤后就被其他线程抢占了,直到其他所有线程打印完之后才能进行后面的步骤。
使用 Lambda 表达式启动线程
当线程所要执行的代码非常短小时,你没有必要专门为之创建一个函数,取而代之的是使用 Lambda表达式。我们可以很轻易地将上述例子改写为使用 Lambda 表达式的形式:
如上,我们使用了 Lambda 表达式替换掉原来的函数指针。毋庸置疑,这段代码和之前使用函数指针的代码实现了完全相同的功能。
启动线程
启动一个新的线程非常简单,当你创建一个 std::thread 的实例时,它便会自行启动。创建线程实例时,必须提供该线程将要执行的函数,方法之一是传递一个函数指针,让我们以经典的
"Hello world" 来阐释这一方法:
#include <thread> #include <iostream> void hello(){ std::cout << "Hello from thread " << std::endl; } int main(){ std::thread t1(hello); t1.join(); return 0; }
所有的线程工具均置于头文件 <thread> 中。这个例子中值得注意的是对函数 join() 的调用。该调用将导致当前线程等待被 join 的线程结束(在本例中即线程 main 必须等待线程 t1 结束后方可继续执行)。如果你忽略掉对 join() 的调用,其结果是未定义的
—— 程序可能打印出 "Hello from thread" 以及一个换行,或者只打印出 "Hello from thread" 却没有换行,甚至什么都不做,那是因为线程main 可能在线程 t1 结束之前就返回了。
区分线程
每个线程都有唯一的 ID 以便我们加以区分。使用 std::thread 类的 get_id() 便可获取标识对应线程的唯一 ID。我们可以使用 std::this_thread 来获取当前线程的引用。下面的例子将创建一些线程并使它们打印自己的 ID:
#include <thread> #include <iostream> #include <vector> void hello(){ std::cout << "Hello from thread " << std::this_thread::get_id() << std::endl; } int main(){ std::vector<std::thread> threads; for(int i = 0; i < 5; ++i){ threads.push_back(std::thread(hello)); } for(auto& thread : threads){ thread.join(); } return 0; }
依次启动线程并将他们存入 vector 是管理多个线程的常用伎俩,这样你便可以轻松地改变线程的数量。回到正题,就算是上面这样短小简单的例子,也不能断定其输出结果。理论情况是:
Hello from thread 140276650997504
Hello from thread 140276667782912
Hello from thread 140276659390208
Hello from thread 140276642604800
Hello from thread 140276676175616
但实际上(至少在我这里)上述情况并不常见,你很可能得到的是如下结果:
Hello from thread Hello from thread Hello from thread 139810974787328Hello from thread 139810983180032Hello from thread
139810966394624
139810991572736
139810958001920
或者更多其他的结果。这是因为线程之间存在 interleaving 。你没办法控制线程的执行顺序,某个线程可能随时被抢占,又因为输出到 ostream 分几个步骤(首先输出一个 string,然后是 ID,最后输出换行),因此一个线程可能执行了第一个步骤后就被其他线程抢占了,直到其他所有线程打印完之后才能进行后面的步骤。
使用 Lambda 表达式启动线程
当线程所要执行的代码非常短小时,你没有必要专门为之创建一个函数,取而代之的是使用 Lambda表达式。我们可以很轻易地将上述例子改写为使用 Lambda 表达式的形式:
#include <thread> #include <iostream> #include <vector> int main(){ std::vector<std::thread> threads; for(int i = 0; i < 5; ++i){ threads.push_back(std::thread([](){ std::cout << "Hello from thread " << std::this_thread::get_id() << std::endl; })); } for(auto& thread : threads){ thread.join(); } return 0; }
如上,我们使用了 Lambda 表达式替换掉原来的函数指针。毋庸置疑,这段代码和之前使用函数指针的代码实现了完全相同的功能。
相关文章推荐
- 【C++11 并发编程教程 - Part 1 : thread 初探(bill译)】
- 【C++11 并发编程教程 - Part 2 : 保护共享数据(bill译)】
- C++11 并发编程教程 - Part 2 : 保护共享数据
- C++11 并发编程教程 - Part 3 : 锁的进阶与条件变量
- 【C++11 并发编程教程 - Part 3 : 锁的进阶与条件变量(bill译)】
- C++11 并发编程教程&学习笔记
- 并发编程: c++11 thread(Func, Args...)利用类成员函数创建线程
- C++11 并发指南二(std::thread 详解)
- Java并发编程:Thread类的使用
- iOS多线程编程Part 1/3 - NSThread & Run Loop
- C++11 并发教程第二部分:保护共享数据
- 初探C++11 Thread
- 使用Python中的greenlet包实现并发编程的入门教程
- C++11 并发指南二(std::thread 详解)
- 【Java并发编程】之六:Runnable和Thread实现多线程的区别(含代码)
- C++11 并发教程——Part2:保护共享数据
- iOS多线程编程Part 1/3 - NSThread & Run Loop
- Java并发编程:Thread类的使用
- MATLAB 入门教程六:编程风格与m文件 取自http://www.matlabsky.com/forum-viewthread-tid-17682-fromuid-88314.html
- C++11 并发指南二(std::thread 详解)