C++ Boost Thread线程使用示例详解
一、并行编程
以下库支持并行编程模型。
- Boost.Thread 允许您创建和管理自己的线程。
- Boost.Atomic 允许您通过多个线程的原子操作访问整数类型的变量。
- Boost.Lockfree 提供线程安全的容器。
- Boost.MPI 起源于超级计算机领域。使用 Boost.MPI,您的程序可以多次启动并在多个进程中执行。您专注于对应该并发执行的实际任务进行编程,而 Boost.MPI 会协调这些过程。使用 Boost.MPI,您无需处理诸如同步访问共享数据之类的细节。但是,Boost.MPI 确实需要适当的运行时环境。
二、生成何管理Threads
这个库中最重要的类是 boost::thread,它在 boost/thread.hpp 中定义。此类用于创建新线程。示例 44.1 是一个创建线程的简单示例。
例 44.1。使用 boost::thread
#include <boost/thread.hpp> #include <boost/chrono.hpp> #include <iostream> void wait(int seconds) { boost::this_thread::sleep_for(boost::chrono::seconds{seconds}); } void thread() { for (int i = 0; i < 5; ++i) { wait(1); std::cout << i << '\n'; } } int main() { boost::thread t{thread}; t.join(); }
新线程应该执行的函数的名称被传递给 boost::thread 的构造函数。一旦示例 44.1 中的变量 t 被创建,函数 thread() 立即开始在它自己的线程中执行。此时,thread() 与 main() 函数同时执行。
为了防止程序终止,在新创建的线程上调用 join()。 join() 阻塞当前线程,直到为其调用 join() 的线程终止。这会导致 main() 等待直到 thread() 返回。
可以使用变量访问特定线程 - 在本示例中为 t t - 以等待其终止。但是,即使 t 超出范围并被销毁,线程仍将继续执行。线程一开始总是绑定到 boost::thread 类型的变量,但一旦创建,线程就不再依赖于该变量。甚至还有一个名为 detach() 的成员函数,它允许类型为 boost::thread 的变量与其对应的线程分离。不可能在调用 detach() 之后调用像 join() 这样的成员函数,因为分离的变量不再代表有效的线程。
任何可以在函数内完成的事情也可以在线程内完成。归根结底,线程与函数没有什么不同,只是它与另一个函数并发执行。在例 44.1 中,循环中将五个数字写入标准输出流。为了减慢输出速度,循环的每次迭代都会调用 wait() 函数来暂停一秒钟。 wait() 使用函数 sleep_for() ,它也由 Boost.Thread 提供并位于命名空间 boost::this_thread 中。
sleep_for() 需要一个时间段作为其唯一参数,该时间段指示当前线程应该停止多长时间。通过传递类型为 boost::chrono::seconds 的对象,可以设置一段时间。 boost::chrono::seconds 来自第 37 章介绍的 Boost.Chrono。
sleep_for() 只接受来自 Boost.Chrono 的类型。尽管 Boost.Chrono 已成为 C++11 标准库的一部分,但来自 std::chrono 的类型不能与 Boost.Thread 一起使用。这样做会导致编译器错误。
如果您不想在 main() 结束时调用 join(),您可以使用类 boost::scoped_thread。
示例 44.2。使用 boost::scoped_thread 等待线程
#include <boost/thread.hpp> #include <boost/thread/scoped_thread.hpp> #include <boost/chrono.hpp> #include <iostream> void wait(int seconds) { boost::this_thread::sleep_for(boost::chrono::seconds{seconds}); } void thread() { for (int i = 0; i < 5; ++i) { wait(1); std::cout << i << '\n'; } } int main() { boost::scoped_thread<> t{boost::thread{thread}}; }
boost::scoped_thread 的构造函数需要一个 boost::thread 类型的对象。在 boost::scoped_thread 的析构函数中,一个动作可以访问该对象。默认情况下,boost::scoped_thread 使用在线程上调用 join() 的操作。因此,示例 44.2 的工作方式类似于示例 44.1。
您可以将用户定义的操作作为模板参数传递。该操作必须是一个带有运算符 operator() 的类,该运算符接受 boost::thread 类型的对象。 boost::scoped_thread 保证运算符将在析构函数中调用。
您只能在 Boost.Thread 中找到类 boost::scoped_thread。标准库中没有对应的。确保包含 boost::scoped_thread 的头文件 boost/thread/scoped_thread.hpp。
示例 44.3 引入了中断点,这使得中断线程成为可能。中断点仅由 Boost.Thread 支持,标准库不支持。
示例 44.3。 boost::this_thread::sleep_for() 的中断点
#include <boost/thread.hpp> #include <boost/chrono.hpp> #include <iostream> void wait(int seconds) { boost::this_thread::sleep_for(boost::chrono::seconds{seconds}); } void thread() { try { for (int i = 0; i < 5; ++i) { wait(1); std::cout << i << '\n'; } } catch (boost::thread_interrupted&) {} } int main() { boost::thread t{thread}; wait(3); t.interrupt(); t.join(); }
在线程对象上调用 interrupt() 会中断相应的线程。在此上下文中,中断意味着在线程中抛出类型为 boost::thread_interrupted 的异常。但是,这仅在线程到达中断点时发生。
如果给定的线程不包含中断点,则简单地调用 interrupt() 不会有任何效果。每当线程到达中断点时,它都会检查是否已调用 interrupt()。如果它已被调用,将抛出 boost::thread_interrupted 类型的异常。
Boost.Thread 定义了一系列中断点,例如 sleep_for() 函数。因为在示例 44.3 中 sleep_for() 被调用了五次,线程检查了五次它是否被中断。在对 sleep_for() 的调用之间,线程不能被中断。
示例 44.3 没有显示五个数字,因为在 main() 中三秒后调用了 interrupt()。因此,相应的线程被中断并抛出 boost::thread_interrupted 异常。即使捕获处理程序为空,异常也会在线程内被正确捕获。因为 thread() 函数在处理程序之后返回,所以线程也会终止。反过来,这将导致程序终止,因为 main() 正在等待线程终止。
Boost.Thread 定义了大约十五个中断点,包括 sleep_for()。这些中断点使得及时中断线程变得容易。然而,中断点可能并不总是最好的选择,因为它们必须在线程可以检查 boost::thread_interrupted 异常之前到达。
示例 44.4。使用 disable_interruption 禁用中断点
#include <boost/thread.hpp> #include <boost/chrono.hpp> #include <iostream> void wait(int seconds) { boost::this_thread::sleep_for(boost::chrono::seconds{seconds}); } void thread() { boost::this_thread::disable_interruption no_interruption; try { for (int i = 0; i < 5; ++i) { wait(1); std::cout << i << '\n'; } } catch (boost::thread_interrupted&) {} } int main() { boost::thread t{thread}; wait(3); t.interrupt(); t.join(); }
类 boost::this_thread::disable_interruption 防止线程被中断。如果实例化 boost::this_thread::disable_interruption,只要对象存在,线程中的中断点就会被禁用。因此,示例 44.4 显示了五个数字,因为中断线程的尝试被忽略了。
示例 44.5。使用 boost::thread::attributes 设置线程属性
#include <boost/thread.hpp> #include <boost/chrono.hpp> #include <iostream> void wait(int seconds) { boost::this_thread::sleep_for(boost::chrono::seconds{seconds}); } void thread() { try { for (int i = 0; i < 5; ++i) { wait(1); std::cout << i << '\n'; } } catch (boost::thread_interrupted&) {} } int main() { boost::thread::attributes attrs; attrs.set_stack_size(1024); boost::thread t{attrs, thread}; t.join(); }
boost::thread::attributes 用于设置线程属性。在 1.56.0 版本中,您只能设置一个与平台无关的属性,即堆栈大小。在示例 44.5 中,堆栈大小由 boost::thread::attributes::set_stack_size() 设置为 1024 字节。
示例 44.6。检测线程 ID 和可用处理器的数量
#include <boost/thread.hpp> #include <iostream> int main() { std::cout << boost::this_thread::get_id() << '\n'; std::cout << boost::thread::hardware_concurrency() << '\n'; }
在命名空间 boost::this_thread 中,定义了适用于当前线程的独立函数。其中一个函数是我们之前见过的 sleep_for()。另一个是 get_id(),它返回一个数字以唯一标识当前线程(参见示例 44.6)。 get_id() 也作为类 boost::thread 的成员函数提供。
静态成员函数 boost::thread::hardware_concurrency() 返回物理上可以同时执行的线程数,基于 CPU 或 CPU 内核的基础数量。在双核处理器上调用此函数返回值 2。此函数提供了一种简单的方法来确定理论上应该使用的最大线程数。
Boost.Thread 还提供类 boost::thread_group 来管理组中的线程。此类提供的一个函数是成员函数 join_all(),它等待组中的所有线程终止。
练习
使用两个线程计算在 for 循环中相加的所有数字的总和:
#include <boost/timer/timer.hpp> #include <iostream> #include <cstdint> int main() { boost::timer::cpu_timer timer; std::uint64_t total = 0; for (int i = 0; i < 1'000'000'000; ++i) total += i; std::cout << timer.format(); std::cout << total << '\n'; }
概括该程序,使其使用尽可能多的线程,可以在计算机上并发执行。例如,如果程序在具有四核 CPU 的计算机上运行,则该程序应该使用四个线程。
到此这篇关于C++ Boost Thread线程使用示例详解的文章就介绍到这了,更多相关C++ Boost Thread内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
最新评论