探索C++中的线程管理(C++ Concurrency in Action 第二章总结)

探索C++中的线程管理(C++ Concurrency in Action 第二章总结)

1.多线程基础

在C++中,std::thread类是进行多线程编程的核心工具。使用std::thread,我们可以轻松地创建和管理多个线程,并让它们并行执行任务

#include <iostream>
#include <thread>
using namespace std;
auto do_something() -> void {
  cout << format("Hello,mutithread!\n");
}
class Task {
 public:
  void operator()() {
    cout << "show in a class ";
    do_something();
  }
};  // 用类提交任务
auto main() -> int {
  cout << format("this is main\n");
  thread t1(do_something);
  thread t2{Task()};  //在传无名类时避免恶心人的初始化解析
  thread t3([] { do_something(); });
  t1.join();  // 主线程等待t1完成
  t2.join();
  t3.detach();  // t3 在后台完成 注意:这种写法要考虑其中变量的声明周期有没有依赖于其他线程!
  //特别是对临时变量的引用之类的不要用
  return 0;
}

在这段代码中,t1和t2使用join来确保主线程等待它们的完成,而t3则使用detach在后台运行,这意味着主线程不会等待t3,它将在后台自行完成任务。这种设计对于那些不依赖其他线程的独立任务非常有效,但需要特别注意分离线程中使用的资源生命周期问题。

2.thread_guard提供join的自动管理

c++20中,std::jthread可以起到thread_guard的作用,但在此之前,需要自己实现。后面还会更进一步讲scoped_thread

// 在发生异常时,必须确保退出时所有其他线程都得到妥善的资源管理,根据RAII,可以利用一个Thread_Guard类设计解决
#include <exception>
#include <iostream>
#include <stdexcept>
#include <thread>
using namespace std;
auto do_something() -> void {
  cout << format("Hello,mutithread!\n");
}
class Thread_Guard {
 public:
  explicit Thread_Guard(thread& t) : t_(t) {}
  ~Thread_Guard() {
    if (t_.joinable()) {
      t_.join();
      cout << format("finish join!\n");
    }
  }
  Thread_Guard(thread const& t)            = delete;
  Thread_Guard& operator=(thread const& t) = delete;
  Thread_Guard(thread&& t)                 = delete;
  Thread_Guard& operator=(thread&& t)      = delete;

 private:
  thread& t_;
};
auto main() -> int {
  try {
    thread t1(do_something);
    Thread_Guard guard_of_t1(t1);
    throw runtime_error("this is an exception");
    //在发生异常后,会最先调用Thread_Guard的析构函数,然后才析构t1,非常巧妙
    t1.join();  //不会调用
  } catch (exception& e) {
    // 可以选择在这里join,但可能有遗漏
    cout << e.what();
  }

  return 0;
}

3. 在C++中传递线程参数

当我们需要在启动线程时传递参数时,需要考虑参数的类型和值的传递方式。C++的std::thread会将参数复制到内部存储中,然后以右值的形式传递给线程函数,这就要求我们在传递资源管理敏感的对象时,必须非常小心。

下列代码中,oops代表错误的写法

#include <iostream>
#include <string>
#include <thread>
using namespace std;
//向线程函数传递参数:在初始化thread时传递,参数会先复制到内部储存中,然后以右值形式传递,所以要注意语义的转换
auto do_something(int num, string const& str) -> void {
  cout << format("{}{}\n", num, str);
}
// auto oops(int num) -> void {
//   char buffer[200];
//   sprintf(buffer, "%i", num);
//   std::thread t(do_something, 3, buffer);  //buffer传参是要转换成string,可能在转换前oops函数就退出,导致未定义行为
//   t.detach();
// }
auto not_oops(int num) -> void {
  char buffer[200];
  sprintf(buffer, "%i", num);
  std::thread t(do_something, 3, std::string(buffer));  //正确的写法
  t.detach();
}
/*
#include <functional>
void update_data_for_widget(widget_id w, widget_data& data) {
  
}
void oops_again(widget_id w) {
  widget_data data;
  std::thread t(update_data_for_widget, w, data);// 传递的还是右值,对于引用类型不能编译
  display_status();
  t.join();
  process_widget_data(data);
}
void not_oops_again(widget_id w) {
  widget_data data;
  std::thread t(update_data_for_widget, w, std::ref(data));
  display_status();
  t.join();
  process_widget_data(data);
}
*/

/*thread传参和std::bind操作定义基于相同的机制,所以有
在新线程上调用 my_x.do_lengthy_work(),因为提供了 my_x 的地址作为对象指针。
还可以为这种成员函数调用提供参数:std::thread 构造函数的第三个参数将成为成员函数的第一个参数,依此类推。
*/
class X {
 public:
  void do_lengthy_work();
};
void f() {
  X my_x;
  std::thread t(&X::do_lengthy_work, &my_x);
}

auto main() -> int {
  // oops(1);
  not_oops(2);
  std::this_thread::sleep_for(std::chrono::seconds(1));
  return 0;
}

4. 线程的所有权转移

在一些复杂的场景中,我们可能需要在不同的函数之间转移线程的所有权。C++中的std::thread类是不可复制的,但可以通过移动语义来转移所有权

#include <iostream>
#include <thread>
using namespace std;
auto some_function() -> void {
  cout << "some_function\n";
}
auto some_other_function() -> void {
  cout << "some_other_function\n";
}
auto f() -> thread {
  return thread(some_function);
}
auto g() -> thread {
  std::thread t(some_other_function);
  return t;
}
//由于移动的特性,可以写出thread_guard的升级版
class scoped_thread {
  std::thread t_;

 public:
  explicit scoped_thread(std::thread t) : t_(std::move(t)) {
    if (!t.joinable()) {
      throw std::logic_error("No thread");
    }
  }
  ~scoped_thread() {
    t_.join();
  }
  scoped_thread(scoped_thread const&)            = delete;
  scoped_thread& operator=(scoped_thread const&) = delete;
};
void f2() {
  std::vector<std::thread> threads;
  for (unsigned i = 0; i < 20; ++i) {
    threads.emplace_back(some_function, i);
  }
  for (auto& entry : threads) {
    entry.join();
  }
}
auto main() -> int {
  thread t1(some_function);
  thread t2(move(t1));
  t1 = thread(some_other_function);
  thread t3(move(t2));
  t1 = move(t3);
}

5. 使用vector等容器管理线程组以及std::thread::hardware_concurrency()特性

在并行计算中,合理地划分任务并分配给多个线程执行可以显著提升程序性能。C++提供了**std::thread::hardware_concurrency()**来帮助我们确定合理的线程数量

下面是并行化的std::accumulate代码示例:

#include <algorithm>
#include <iostream>
#include <numeric>
#include <thread>
template <typename Iterator, typename T>
struct accumulate_block {
  void operator()(Iterator first, Iterator last, T& result) {
    result = std::accumulate(first, last, result);
  }
};

template <typename Iterator, typename T>
T parallel_accumulate(Iterator first, Iterator last, T init) {
  unsigned long const length = std::distance(first, last);
  if (!length) {
    return init;
  }
  unsigned long const min_per_thread   = 25;
  unsigned long const max_threads      = (length + min_per_thread - 1) / min_per_thread;
  unsigned long const hardware_threads = std::thread::hardware_concurrency();
  unsigned long const num_threads      = std::min(hardware_threads != 0 ? hardware_threads : 2, max_threads);
  unsigned long const block_size       = length / num_threads;
  std::vector<T> results(num_threads);
  std::vector<std::thread> threads(num_threads - 1);
  Iterator block_start = first;
  for (unsigned long i = 0; i < (num_threads - 1); ++i) {
    Iterator block_end = block_start;
    std::advance(block_end, block_size);
    threads[i]  = std::thread(accumulate_block<Iterator, T>(), block_start, block_end, std::ref(results[i]));
    block_start = block_end;
  }
  accumulate_block<Iterator, T>()(block_start, last, results[num_threads - 1]);

  for (auto& entry : threads) {
    entry.join();
  }
  return std::accumulate(results.begin(), results.end(), init);
}
auto main() -> int {
  std::cout << std::thread::hardware_concurrency();
}
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值