C++ 多線程:初學者指南

你好,未來的編程超級巨星!很高興能成為你在這個令人興奮的C++多線程世界之旅的導遊。作為一個教編程多年的老師,我可以向您保證,這個主題一開始可能會讓人感到畏難,但一旦您掌握了它,其實非常吸引人。所以,讓我們捲起袖子,深入探險吧!

C++ Multithreading

什麼是多線程?

在我們深入細節之前,先從基礎開始。想象一下您正在廚房裡,試圖準備一頓複雜的飯菜。您可以一步一步地做——先切蔬菜,然後煮義大利麵,然後準備醬汁。但如果是同時進行這些工作,不是更有效率嗎?這基本上就是多線程在我們的程序中所做的事情!

多線程允許程序同時執行多個任務。每個任務都被稱為一個“線程”。這就像廚房中有多位廚師,每個人負責飯菜的不同部分。

現在,讓我們看看如何在C++中利用這種力量!

創建線程

在C++中創建線程就像為我們的廚房聘請一位新的廚師。我們需要告訴這位廚師(線程)要執行哪項任務。在C++中,我們使用<thread>庫中的std::thread類來做到這一點。

來看看一個簡單的例子:

#include <iostream>
#include <thread>

void cookPasta() {
std::cout << "Cooking pasta..." << std::endl;
}

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

在這個例子中:

  1. 我們包含了必要的庫:<iostream>用於輸入/輸出,<thread>用於多線程。
  2. 我們定義了一個函數cookPasta(),我們的線程將執行它。
  3. main()中,我們創建了一個名為chefOne的線程,並告訴它執行cookPasta()函數。
  4. 我們使用join()在程序結束之前等待線程完成其任務。

當您運行此程序時,您將會在控制台上看到“Cooking pasta...”的輸出。恭喜!您剛剛創建了您的第一個線程!

結束線程

現在,如果我們的廚師煮義大利麵花費的時間太長怎麼辦?在編程的世界中,我們可能需要在線程完成其任務之前終止它。但是,重要的是要注意,強行終止線程可能會導致資源洩露和其他問題。通常更好的做法是設計線程自然完成或響應終止信號。

以下是一個如何設置線程以響應終止信號的例子:

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

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

void cookPasta() {
while (!stop_thread) {
std::cout << "Still cooking pasta..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "Pasta cooking stopped!" << 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 << "Cooking " << dish << " for " << time << " minutes." << std::endl;
}

int main() {
std::thread chefOne(cookDish, "Spaghetti", 10);
std::thread chefTwo(cookDish, "Pizza", 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 << " is ready!" << std::endl;
}

void quickCook(std::string dish) {
std::cout << dish << " is ready!" << std::endl;
}

int main() {
std::thread slowChef(slowCook, "Stew");
std::thread quickChef(quickCook, "Salad");

slowChef.detach();  // 讓慢廚師在背景工作
quickChef.join();   // 等待快廚師完成

std::cout << "Main program ending. Slow chef might still be working!" << std::endl;
return 0;
}

在這個例子中:

  1. 我們有兩位廚師:一位慢慢烹飪炖菜,一位快速準備沙拉。
  2. 我們分離了慢廚師的線程,讓它可以在背景中繼續工作。
  3. 我們加入快廚師的線程,等待沙拉準備好。
  4. 主程序結束,可能會在炖菜準備好之前。
方法 描述 使用情況
join() 等待線程完成 當您需要在繼續之前獲得線程的結果時
detach() 允許線程獨立運行 對於可以獨立運行的背景任務

就是這樣,各位!您剛剛踏出了C++多線程世界的第一步。請記住,就像學習烹飪一樣,掌握多線程需要練習。不要害怕嘗試這些概念,很快您就能夠像廚房中的大廚一樣,輕鬆地編織出複雜、高效的程序!

編程愉快,願您的線程永遠運行平順!

Credits: Image by storyset