C++ 多线程:初学者指南

你好,未来的编码巨星!我很激动能在这段令人兴奋的旅程中指导你进入C++多线程的世界。作为一个教编程多年的人,我可以向你保证,虽然这个主题一开始可能看起来令人生畏,但一旦你掌握了它,实际上是非常吸引人的。所以,让我们卷起袖子,深入其中吧!

C++ Multithreading

什么是多线程?

在我们深入研究细节之前,让我们从基础开始。想象一下你在一个厨房里,试图准备一顿复杂的饭菜。你可以一步一步来做 —— 先切蔬菜,然后煮面,然后准备酱汁。但如果你能同时做这些任务,那不是更有效率吗?这正是多线程为我们的程序所做的!

多线程允许程序同时执行多个任务。这些任务中的每一个都被称为一个“线程”。这就像厨房里有多个厨师,每个厨师负责餐点的一部分。

现在,让我们看看如何在C++中利用这种力量!

创建线程

在C++中创建一个线程就像是为我们的厨房雇佣一个新的厨师。我们需要告诉这个厨师(线程)要执行什么任务。在C++中,我们使用<thread>库中的std::thread类来实现这一点。

让我们看一个简单的例子:

#include <iostream>
#include <thread>

void cookPasta() {
std::cout << "正在煮面..." << std::endl;
}

int main() {
std::thread chefOne(cookPasta);
chefOne.join();
return 0;
}

在这个例子中:

  1. 我们包含了必要的库:<iostream>用于输入/输出,<thread>用于多线程。
  2. 我们定义了一个函数cookPasta(),我们的线程将执行它。
  3. main()中,我们创建了一个名为chefOne的线程,并告诉它执行cookPasta()函数。
  4. 我们使用join()来等待线程完成任务后再结束程序。

当你运行这个程序时,你会看到“正在煮面...”打印在控制台上。恭喜你!你已经创建了你的第一个线程!

终止线程

现在,如果我们的厨师煮面花了太长时间怎么办?在编程的世界里,我们可能需要在线程完成其任务之前终止它。但是,需要注意的是,强制终止线程可能会导致资源泄漏和其他问题。通常更好的做法是设计线程自然完成或响应终止信号。

以下是一个如何设置线程以响应终止信号的例子:

#include <iostream>
#include <thread>
#include <atomic>

std::atomic<bool> stop_thread(false);

void cookPasta() {
while (!stop_thread) {
std::cout << "还在煮面..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "煮面已停止!" << std::endl;
}

int main() {
std::thread chefOne(cookPasta);

std::this_thread::sleep_for(std::chrono::seconds(5));
stop_thread = true;

chefOne.join();
return 0;
}

在这个例子中:

  1. 我们使用一个atomic<bool>变量stop_thread来在线程之间安全地通信。
  2. 我们的cookPasta()函数现在在循环中检查这个变量。
  3. main()中,我们让线程运行5秒钟,然后设置stop_thread为true。
  4. 线程通过完成其循环并自然结束来响应。

向线程传递参数

如果我们想给我们的厨师更具体的指示怎么办?在C++中,我们可以像传递函数参数一样传递参数给我们的线程。让我们看看如何操作:

#include <iostream>
#include <thread>
#include <string>

void cookDish(std::string dish, int time) {
std::cout << "正在煮 " << dish << ",需要 " << time << " 分钟。" << std::endl;
}

int main() {
std::thread chefOne(cookDish, "意大利面", 10);
std::thread chefTwo(cookDish, "披萨", 15);

chefOne.join();
chefTwo.join();

return 0;
}

在这个例子中:

  1. 我们的cookDish()函数现在接受两个参数:菜肴名称和烹饪时间。
  2. 我们创建两个线程,每个线程烹饪不同的菜肴,时间也不同。
  3. 我们在创建线程时直接传递这些参数。

这展示了线程有多么灵活 —— 我们可以有多个线程执行类似的任务,但使用不同的参数!

加入和分离线程

最后,让我们谈谈两个重要的概念:加入和分离线程。

加入线程

我们已经在之前的例子中看到了join()。当我们在线程上调用join()时,我们告诉主程序在继续之前等待该线程完成。这就像是在上菜之前等待厨师完成准备一道菜。

分离线程

有时,我们可能想让线程独立运行,而不等待它完成。这时detach()就派上用场了。一个分离的线程将继续在后台运行,即使主程序结束。

以下是一个说明两者用法的例子:

#include <iostream>
#include <thread>
#include <chrono>

void slowCook(std::string dish) {
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << dish << " 准备好了!" << std::endl;
}

void quickCook(std::string dish) {
std::cout << dish << " 准备好了!" << std::endl;
}

int main() {
std::thread slowChef(slowCook, "炖菜");
std::thread quickChef(quickCook, "沙拉");

slowChef.detach();  // 让慢厨师在后台工作
quickChef.join();   // 等待快厨师完成

std::cout << "主程序结束。慢厨师可能还在工作!" << std::endl;
return 0;
}

在这个例子中:

  1. 我们有两个厨师:一个慢慢炖菜,一个快速准备沙拉。
  2. 我们分离了慢厨师的线程,允许它在后台继续工作。
  3. 我们加入了快厨师的线程,等待沙拉准备好。
  4. 主程序结束,可能在炖菜准备好之前。
方法 描述 使用场景
join() 等待线程完成 当你需要线程的结果才能继续时
detach() 允许线程独立运行 对于可以独立运行的背景任务

就这样,各位!你已经迈出了进入C++多线程世界的第一步。记住,就像学习烹饪一样,掌握多线程需要练习。不要害怕尝试这些概念,很快你就能像厨房里的主厨一样,轻松地编写出复杂、高效的程序!

编码愉快,愿你的线程永远运行顺畅!

Credits: Image by storyset