Boost.Thread-创建和管理线程-The Boost C++ Libraries

The Boost C++ Libraries

创建和管理线程

在Boost.Thread库中最重要的类是boost ::thread,它在boost/thread.hpp中定义。 该类用于创建新线程。 Example 44.1是一个创建线程的简单示例。

Example 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超出范围并被破坏,线程也将继续执行。一开始,线程始终绑定到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类。

Example 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支持,而不受标准库支持。

Example 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异常。即使catch处理程序为空,也可以在线程内部正确捕获异常。由于thread()函数在处理程序之后返回,因此线程也会终止。反过来,这将导致程序终止,因为main()正在等待线程终止。

Boost.Thread定义了大约15个中断点,包括sleep_for()。这些中断点使及时中断线程变得容易。但是,中断点不一定总是最好的选择,因为在线程可以检查boost::thread_interrupted异常之前必须达到中断点。

Example 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显示了五个数字,因为忽略了中断线程的尝试。

Example 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字节。

Example 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)。 类boost::thread也将get_id()作为成员函数提供。

静态成员函数boost::thread::hardware_concurrency()根据CPU或CPU核心的基础数量返回可以在物理上同时执行的线程数。 在双核处理器上调用此函数将返回值2。此函数提供了一种简单的方法来确定理论上应使用的最大线程数。

Boost.Thread还提供了boost::thread_group类来管理组中的线程。 此类提供的一个函数,成员函数join_all(),等待组中的所有线程终止。

练习

1. 在下面的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';
}

2.对该程序进行一般化,以使其使用尽可能多的线程可以在计算机上同时执行。 例如,如果该程序在带有四核CPU的计算机上运行,则应使用四个线程。

上一篇 下一篇

猜你喜欢

转载自blog.csdn.net/ccf19881030/article/details/106029851