C++ Boost Thread线程使用示例详解

 更新时间:2022年11月18日 10:08:37   作者:无水先生  
Boost是为C++语言标准库提供扩展的一些C++程序库的总称。Boost库是一个可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的开发引擎之一,是为C++语言标准库提供扩展的一些C++程序库的总称

一、并行编程

以下库支持并行编程模型。

  • 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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解C语言的基本数据类型

    详解C语言的基本数据类型

    这篇文章主要为大家介绍了C语言的基本数据类型,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • C语言深入浅出讲解直接插入排序算法的实现

    C语言深入浅出讲解直接插入排序算法的实现

    插入排序也是最简单的一类排序方法,我今天介绍的也是插入排序里最直观且浅显易懂的直接插入排序。对这个很简单的排序,记得当时也是花了近两个晚上才搞懂它的原理的,接下来就来介绍一下
    2022-05-05
  • 分析C语言一个简单程序

    分析C语言一个简单程序

    本文主要介绍C语言简单的程序,这里给大家详细介绍C语言代码,对函数概念、头文件、自定义函数等基础信息的讲解,希望能帮助刚刚学习的同学
    2016-07-07
  • c++  复制消除问题解决示例详析

    c++  复制消除问题解决示例详析

    这篇文章主要为大家介绍了c++  复制消除问题解决示例详析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • C++德州扑克的核心规则算法

    C++德州扑克的核心规则算法

    德州扑克想必很多人都玩过,当然对于新手需要说明的是,德州指的是德克萨斯州,而不是山东德州。分享下在计算最大牌型,比牌逻辑的算法和洗牌的方法,希望对大家有帮助。
    2016-04-04
  • 一篇文章弄懂C++左值引用和右值引用

    一篇文章弄懂C++左值引用和右值引用

    左值(lvalue)和右值(rvalue)是 c/c++ 中一个比较晦涩基础的概念,这篇文章主要给大家介绍了关于如何通过一篇文章弄懂C++左值引用和右值引用的相关资料,需要的朋友可以参考下
    2021-07-07
  • C/C++ Qt StatusBar底部状态栏应用教程

    C/C++ Qt StatusBar底部状态栏应用教程

    Qt窗体中默认会附加一个QstatusBar组件,状态栏组件位于主窗体的最下方,其作用是提供一个工具提示功能。本文主要介绍了StatusBar底部状态栏的应用教程,需要的同学可以学习一下
    2021-12-12
  • C++基础教程之指针拷贝详解

    C++基础教程之指针拷贝详解

    这篇文章主要介绍了C++基础教程之指针拷贝详解的相关资料,需要的朋友可以参考下
    2017-01-01
  • OpenGL绘制Bezier曲线的方法

    OpenGL绘制Bezier曲线的方法

    这篇文章主要为大家详细介绍了OpenGL绘制Bezier曲线的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • 关于C/C++中typedef的定义与用法总结

    关于C/C++中typedef的定义与用法总结

    在C还是C++代码中,typedef都使用的很多,在C代码中尤其是多,typedef与#define有些相似,其实是不同的,特别是在一些复杂的用法上,需要的朋友可以参考下
    2012-12-12

最新评论