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

目录
  • 一、并行编程
  • 二、生成何管理Threads
  • 练习

一、并行编程

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

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

(0)

相关推荐

  • C++ Boost Atomic详细讲解

    目录 一.说明 二.示例和代码 一.说明 Boost.Atomic 提供类 boost::atomic,可用于创建原子变量.它们被称为原子变量,因为所有访问都是原子的. Boost.Atomic 用于多线程程序,当在一个线程中访问变量不应被访问相同变量的另一个线程中断时.如果没有 boost::atomic,从多个线程访问共享变量的尝试将需要与锁同步. boost::atomic 取决于支持原子变量访问的目标平台.否则,boost::atomic 使用锁.该库允许您检测目标平台是否支持原子变量访

  • C++ Boost EnableIf函数使用介绍

    目录 一.说明 二.Boost.EnableIf的示例 练习 一.说明 Boost.EnableIf Boost.Enable If 可以禁用重载函数模板或专用类模板.禁用意味着编译器忽略相应的模板.这有助于防止出现模棱两可的情况,即编译器不知道要使用哪个重载函数模板.它还可以更轻松地定义不仅可用于特定类型而且可用于一组类型的模板. 从 C++11 开始,Boost.EnableIf 已经成为标准库的一部分.您可以在不使用 Boost 库的情况下调用本章介绍的函数:只需包含头文件 type_tr

  • C++ Boost Coroutine使用协程详解

    目录 一.说明语言扩展 二.库Boost.Coroutine 三.示例和代码 一.说明语言扩展 以下库扩展了编程语言 C++. Boost.Coroutine 使得在 C++ 中使用协程成为可能——其他编程语言通常通过关键字 yield 支持. Boost.Foreach 提供了一个基于范围的 for 循环,它是在 C++11 中添加到语言中的. Boost.Parameter 允许您以名称/值对的形式并以任何顺序传递参数——例如,这在 Python 中是允许的. Boost.Conversio

  • C++ Boost Fusion创建异构容器详解

    目录 一.说明 二.示例和代码 一.说明 标准库提供了许多容器,它们有一个共同点:它们是同类的.也就是说,标准库中的容器只能存储一种类型的元素. std::vector<int> 类型的向量只能存储 int 值,而 std::vector<std::string> 类型的向量只能存储字符串. Boost.Fusion 使创建异构容器成为可能.例如,您可以创建一个向量,其第一个元素是 int,第二个元素是字符串.此外,Boost.Fusion 提供了处理异构容器的算法.您可以将 Bo

  • C++ Boost Lockfree超详细讲解使用方法

    目录 一.说明 二.示例和代码 Boost.Lockfree 一.说明 Boost.Lockfree 提供线程安全和无锁容器.可以从多个线程访问此库中的容器,而无需同步访问. 在 1.56.0 版本中,Boost.Lockfree 只提供了两个容器:boost::lockfree::queue 类型的队列和 boost::lockfree::stack 类型的栈.对于队列,可以使用第二个实现:boost::lockfree::spsc_queue.此类针对只有一个线程写入队列和只有一个线程从队列

  • C++ boost thread库用法详细讲解

    目录 一.说明 二.boost::thread的几个函数 三.构造 一.说明 boost::thread的六种使用方法总结,本文初步介绍线程的函数.构造.执行的详细解释. 二.boost::thread的几个函数 函数 功能 join() 让主进程等待子线程执行完毕后再继续执行 get_id() 获得线程的 id 号 detach() 标线程就成为了守护线程,驻留后台运行 bool joinable() 是否已经启动,为 join() thread::join()是个简单暴力的方法,主线程等待子

  • C++ Boost TypeTraits库使用详解

    目录 一.说明 二.库Boost.TypeTraits 一.说明 反省是重点中的重点,泛型在实践中贯穿工程的始终.以下库支持泛型编程.无需详细了解模板元编程即可使用这些库. Boost.TypeTraits 提供了检查类型属性的函数. Boost.EnableIf 可以与 Boost.TypeTraits 一起使用,例如,根据函数的返回类型重载函数. Boost.Fusion 使创建异构容器成为可能——其元素可以具有不同类型的容器. Boost.TypeTraits Boost.EnableIf

  • C++ Boost MPI接口详细讲解

    目录 一.说明 二.开发和运行时环境 三.简单数据交换 一.说明 Boost.MPI 提供了 MPI 标准(消息传递接口)的接口.该标准简化了并发执行任务的程序的开发.您可以使用线程或通过共享内存或网络连接使多个进程相互通信来开发此类程序. MPI 的优点是你不需要关心这些细节.您可以完全专注于并行化您的程序. 缺点是您需要 MPI 运行时环境.如果您控制运行时环境,MPI 只是一个选项.例如,如果你想分发一个可以通过双击启动的程序,你将无法使用 MPI.虽然操作系统开箱即用地支持线程.共享内存

  • C++同步线程实现示例详解

    目录 一.同步线程 二.独占访问示例 一.同步线程 虽然使用多线程可以提高应用程序的性能,但通常也会增加复杂性.如果同时执行多个函数,则必须同步对共享资源的访问.一旦应用程序达到一定大小,这将涉及大量的编程工作.本节介绍Boost.Thread提供的用于同步线程的类. 二.独占访问示例 示例 44.7.使用 boost::mutex 的独占访问 #include <boost/thread.hpp> #include <boost/chrono.hpp> #include <

  • java中常见的6种线程池示例详解

    之前我们介绍了线程池的四种拒绝策略,了解了线程池参数的含义,那么今天我们来聊聊Java 中常见的几种线程池,以及在jdk7 加入的 ForkJoin 新型线程池 首先我们列出Java 中的六种线程池如下 线程池名称 描述 FixedThreadPool 核心线程数与最大线程数相同 SingleThreadExecutor 一个线程的线程池 CachedThreadPool 核心线程为0,最大线程数为Integer. MAX_VALUE ScheduledThreadPool 指定核心线程数的定时

  • Python异步爬虫多线程与线程池示例详解

    目录 背景 异步爬虫方式 多线程,多进程(不建议) 线程池,进程池(适当使用) 单线程+异步协程(推荐) 多线程 线程池 背景 当对多个url发送请求时,只有请求完第一个url才会接着请求第二个url(requests是一个阻塞的操作),存在等待的时间,这样效率是很低的.那我们能不能在发送请求等待的时候,为其单独开启进程或者线程,继续请求下一个url,执行并行请求 异步爬虫方式 多线程,多进程(不建议) 好处:可以为相关阻塞的操作单独开启线程或者进程,阻塞操作就可以异步会执行 弊端:不能无限制开

  • Spring Boot之@Async异步线程池示例详解

    目录 前言 一. Spring异步线程池的接口类 :TaskExecutor 二.简单使用说明 三.定义通用线程池 1.定义线程池 2.异步方法使用线程池 3.通过xml配置定义线程池 四.异常处理 五.问题 前言 很多业务场景需要使用异步去完成,比如:发送短信通知.要完成异步操作一般有两种: 1.消息队列MQ 2.线程池处理. 我们来看看Spring框架中如何去使用线程池来完成异步操作,以及分析背后的原理. 一. Spring异步线程池的接口类 :TaskExecutor 在Spring4中,

  • Java 语言守护线程 Daemon Thread使用示例详解

    目录 守护线程 用户线程设为守护线程 守护线程 在Java语言中有两类线程:用户线程和守护线程.我们通俗的讲,任意一个守护线程都是整个JVM中所有线程的"大管家":只要当前Java虚拟机中还有任意一个非守护线程没有结束,它们的守护线程就不能结束,要持续工作:只有当最后一个非守护线程结束时,守护线程才随着Java虚拟机一起结束工作. 其作用就是为其他线程的运行提供服务,就像是一个护道者,保证其他线程的顺利运行 用户线程设为守护线程 我们将用户线程设为守护线程的办法就是Thread类的se

  • java线程池ThreadPoolExecutor的八种拒绝策略示例详解

    目录 池化设计思想 线程池触发拒绝策略的时机 JDK内置4种线程池拒绝策略 拒绝策略接口定义 AbortPolicy(中止策略) DiscardPolicy(丢弃策略) DiscardOldestPolicy(弃老策略) 第三方实现的拒绝策略 Dubbo 中的线程拒绝策略 Netty 中的线程池拒绝策略 ActiveMQ 中的线程池拒绝策略 PinPoint 中的线程池拒绝策略 谈到 Java 的线程池最熟悉的莫过于 ExecutorService 接口了,jdk1.5 新增的 java.uti

  • c# Thread类线程常用操作详解

    创建线程 线程是通过扩展 Thread 类创建的.扩展的 Thread 类调用 Start() 方法来开始子线程的执行. 下面的程序演示了这个概念: class ThreadCreationProgram { public static void CallToChildThread() { Console.WriteLine("Child thread starts"); } static void Main(string[] args) { ThreadStart childref

  • Golang WorkerPool线程池并发模式示例详解

    目录 正文 处理CVS文件记录 获取测试数据 线程池耗时差异 正文 Worker Pools 线程池是一种并发模式.该模式中维护了固定数量的多个工作器,这些工作器等待着管理者分配可并发执行的任务.该模式避免了短时间任务创建和销毁线程的代价. 在 golang 中,我们使用 goroutine 和 channel 来构建这种模式.工作器 worker 由一个 goroutine 定义,该 goroutine 通过 channel 获取数据. 处理CVS文件记录 接下来让我们通过一个例子,来进一步理

  • spring结合hibernate示例详解

    单纯Hibernate程序 1.首先是导入hibernate的jar包. 2. 建立用户和用户操作记录实体,Log.Java和User.java.代码如下所示. Log.java import java.util.Date; public class Log { private int id; //日志的类别.日志一般起到一个不可否认性. //操作日志 安全日志 事件日志. private String type; private String detail; private Date time

  • Java ExecutorService四种线程池使用详解

    1.引言 合理利用线程池能够带来三个好处.第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗.第二:提高响应速度.当任务到达时,任务可以不需要的等到线程创建就能立即执行.第三:提高线程的可管理性.线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控.但是要做到合理的利用线程池,必须对其原理了如指掌. 2.线程池使用 Executors提供的四种线程 1.newCachedThreadPool创建一个可缓存线程池

随机推荐