您好,登錄后才能下訂單哦!
這篇文章主要介紹“C++中線程的原理與實現方法是什么”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“C++中線程的原理與實現方法是什么”文章能幫助大家解決問題。
在C++中有多種實現線程的方式
C++11提供的標準多線程方式;
第三方庫(如:Boost.Thread);
操作系統提供的多線程(如:Windows 線程 與 POSIX 線程(pthread))。
我們這里先了解的就是C++11提供的標準多線程方式。因為它提供了良好的跨平臺兼容性和簡潔的語法,已經滿足大多數需求。
從最簡單的開始
C++11 引入了多線程支持,提供了一套基本的線程庫,包括線程、互斥量(mutex)、條件變量(condition_variable)等。這些組件可以幫助你在 C++ 程序中實現并發和多線程編程。下面是一些基本概念和示例:
1.std::thread:
std::thread
是 C++11 中的線程類,用于創建和管理線程。您可以將一個函數作為參數傳遞給 std::thread
的構造函數,該函數將在新線程中執行。
#include <iostream> #include <thread> void hello() { std::cout << "Hello from thread!" << std::endl; } int main() { std::thread t(hello); // 創建一個新線程,執行 hello 函數 t.join(); // 等待線程結束 return 0; }
2.std::mutex:
std::mutex
是互斥量類,用于保護共享資源的訪問。當多個線程需要訪問共享資源時,使用互斥量可以確保每次只有一個線程訪問資源,從而避免數據競爭和其他并發問題。
#include <iostream> #include <mutex> #include <thread> std::mutex mtx; // 互斥量 void print_block(int n, char c) { mtx.lock(); // 鎖定互斥量 for (int i = 0; i < n; ++i) { std::cout << c; } std::cout << std::endl; mtx.unlock(); // 解鎖互斥量 } int main() { std::thread t1(print_block, 50, '*'); std::thread t2(print_block, 50, '$'); t1.join(); t2.join(); return 0; }
3.std::lock_guard:
std::lock_guard
是一個 RAII(Resource Acquisition Is Initialization)包裝類,用于自動管理互斥量的鎖定和解鎖。當創建 std::lock_guard
對象時,它將自動鎖定互斥量,當對象銷毀時,它將自動解鎖互斥量。
void print_block(int n, char c) { std::lock_guard<std::mutex> lock(mtx); // 自動鎖定互斥量 for (int i = 0; i < n; ++i) { std::cout << c; } std::cout << std::endl; // 自動解鎖互斥量(lock_guard 對象銷毀時) }
4.std::condition_variable:
std::condition_variable
是一個條件變量類,用于在線程之間同步操作。它可以與 std::mutex
配合使用,實現線程間的等待和通知機制。
#include <iostream> #include <condition_variable> #include <mutex> #include <thread> std::mutex mtx; std::condition_variable cv; bool ready = false; void print_id(int id) { std::unique_lock<std::mutex> lck(mtx); cv.wait(lck, [] { return ready; }); // 等待 ready 變為 true std::cout << "thread " << id << std::endl; } void go() { std::unique_lock<std::mutex> lck(mtx); ready = true; cv.notify_all();// 通知所有等待的線程 } int main() { std::thread threads[10]; for (int i = 0; i < 10; ++i) { threads[i] = std::thread(print_id, i); // 啟動 10 個線程 } go(); // 通知所有線程開始執行 for (auto& th : threads) { th.join(); } return 0; }
在這個示例中,我們創建了 10 個線程,每個線程在啟動后等待一個條件變量。主線程通過調用 go
函數將條件變量的狀態設置為 true
并通知所有等待的線程,使它們開始執行。
5.std::future 和 std::async:
std::future
和 std::async
是 C++11 提供的用于異步操作的類。std::async
可以異步地執行一個函數,并返回一個 std::future
對象,該對象表示該函數的返回值。您可以通過調用 std::future::get()
來等待函數執行完成并獲取其返回值。
#include <iostream> #include <future> int sum(int a, int b) { return a + b; } int main() { std::future<int> result = std::async(sum, 10, 20); // 異步執行 sum 函數 int value = result.get(); // 等待執行完成并獲取返回值 std::cout << "The result is: " << value << std::endl; return 0; }
這個簡單的示例展示了如何使用 std::async
異步地執行一個求和函數,然后通過std::future
獲取其結果。
C++11 的多線程支持功能使得在 C++ 中實現并發編程變得更加簡單。通過這些基本組件,您可以根據需要構建更復雜的并發程序。
C++11的線程只有這么簡單嗎?
是也不是。C++11 中的多線程庫確實相對簡單,但這只是表面現象。實際上,它們為復雜的多線程程序提供了基礎。前面已經介紹了一些基本的多線程組件,例如 std::thread
、std::mutex
、std::condition_variable
、std::future
和 std::async
。但是,還有一些其他的組件和技巧可能會對你有幫助:
1.std::atomic:
C++11 引入了原子類型(std::atomic
),用于實現原子操作,即在多線程環境中不會被中斷的操作。原子類型在多線程中特別有用,因為它們可以避免數據競爭和其他并發問題。
示例:
#include <iostream> #include <atomic> #include <thread> std::atomic<int> counter(0); void increase_counter() { for (int i = 0; i < 1000; ++i) { ++counter; } } int main() { std::thread t1(increase_counter); std::thread t2(increase_counter); t1.join(); t2.join(); std::cout << "Counter: " << counter << std::endl; return 0; }
2.std::call_once:
std::call_once
是一個用于確保在多線程環境中某個函數只被調用一次的工具。它需要一個 std::once_flag
變量作為參數,該變量用于跟蹤函數是否已被調用。
示例:
#include <iostream> #include <mutex> #include <thread> std::once_flag flag; void do_something() { std::cout << "Called once" << std::endl; } void call_do_something() { std::call_once(flag, do_something); } int main() { std::thread t1(call_do_something); std::thread t2(call_do_something); t1.join(); t2.join(); return 0; }
3.線程局部存儲:
C++11 支持線程局部存儲,即每個線程擁有自己的變量副本。使用 thread_local
關鍵字可以定義一個線程局部變量。這對于某些需要每個線程擁有獨立狀態的應用場景非常有用。
示例:
#include <iostream> #include <thread> thread_local int counter = 0; void increase_counter() { ++counter; std::cout << "Counter: " << counter << " in thread " << std::this_thread::get_id() << std::endl; } int main() { std::thread t1(increase_counter); std::thread t2(increase_counter); t1.join(); t2.join(); return 0; }
以上是 C++11 多線程庫中的一些其他組件和技巧。雖然這些組件相對簡單,但它們為實現復雜的多線程應用提供了基礎。掌握這些基本概念后,你可以根據自己的需求組合這些組件以實現更高級的功能。以下是一些可能對你有幫助的高級用法:
線程池
線程池是一種允許您在一組線程中重用線程以執行任務的技術。這可以減少線程創建和銷毀的開銷,從而提高性能。C++11 沒有提供內置的線程池功能,但您可以使用基本的多線程組件自己實現一個,或者使用第三方庫(例如 Boost.Asio)。
并行算法
C++17 標準引入了并行算法庫,它提供了一些與 STL 算法類似的并行版本,以支持多線程并行執行。這使得實現并行計算變得更加簡單。例如,您可以使用 std::sort
的并行版本 std::execution::par
對大數據集進行排序。
示例:
#include <iostream> #include <vector> #include <algorithm> #include <execution> #include <random> int main() { std::vector<int> data(100000); std::random_device rd; std::mt19937 gen(rd()); std::generate(data.begin(), data.end(), [&]() { return gen() % 1000; }); std::sort(std::execution::par, data.begin(), data.end()); // 現在 data 已經被排序 return 0; }
lock_guard 和 scoped_lock:
std::lock_guard
是一個簡化互斥鎖管理的 RAII 封裝。當您創建一個 lock_guard
對象時,它將自動鎖定給定的互斥鎖,并在銷毀時自動解鎖。這有助于避免死鎖和忘記解鎖。
std::scoped_lock
是 C++17 引入的一個改進版的 lock_guard
,用于同時鎖定多個互斥鎖,避免死鎖。
std::shared_mutex 和 std::shared_lock:
std::shared_mutex
是一種特殊類型的互斥鎖,允許多個線程同時以共享模式訪問資源。std::shared_lock
是與 std::shared_mutex
配合使用的鎖對象,允許您在共享模式或獨占模式下鎖定資源。
以上是 C++11 多線程庫的一些高級用法。熟練掌握這些組件和技巧可以幫助您實現更加高效、可擴展和健壯的多線程應用。
大體其實就這些,另外在設計時還需要注意的是:
避免死鎖:
在多線程編程中,死鎖是一個常見的問題,它發生在兩個或多個線程相互等待對方釋放資源時。為了避免死鎖,請確保使用鎖的順序一致,避免嵌套鎖,并盡量減少鎖的使用范圍。
數據競爭與內存模型:
在多線程環境中,數據競爭是一個潛在的問題。當多個線程同時訪問共享數據且至少有一個線程對數據進行修改時,就會發生數據競爭。避免數據競爭的方法包括使用互斥鎖、原子操作或者線程局部存儲。
此外,C++11 引入了內存模型,用于描述多線程中的內存訪問行為。內存模型包括原子操作的內存順序,例如 std::memory_order_relaxed
、std::memory_order_acquire
和 std::memory_order_release
。在大多數情況下,默認的內存順序已經足夠使用,但在某些高級應用場景下,理解和使用內存模型可以幫助您實現更高效的代碼。
性能與可伸縮性:
在編寫多線程程序時,需要權衡性能和可伸縮性。線程之間的通信和同步會導致性能損失,因此您需要在使用更多線程以提高并發性能時,盡量減少同步和通信的開銷。
異常安全:
在多線程環境中,處理異常尤為重要。在一個線程中發生異常時,其他線程可能仍在繼續執行。確保在多線程中正確處理異常,例如使用 try-catch
塊捕獲異常,并確保鎖和資源在異常發生時得到正確的釋放。
第三方庫和框架:
除了 C++ 標準庫提供的多線程支持外,還有一些第三方庫和框架提供了更高級或特定領域的多線程功能。例如,Boost.Thread 庫提供了類似于 C++11 多線程庫的功能,但在某些方面更為強大。Intel 的 Threading Building Blocks (TBB) 是另一個廣泛使用的并行編程庫。
P.S. 再來點與之無關緊要的小知識
join()
既然是等待子線程完成,為什么不叫wait_thread()
之類的? 而且join
本身的單詞是加入,有點感覺格格不入。
因為在多線程編程中,join
方法的命名來源于它的作用:將一個子線程加入(join)到主線程或其他線程,等待這個子線程完成。這種“加入”的概念實際上是指當前線程(通常是主線程)等待另一個線程(子線程)完成它的任務。在子線程完成任務之前,當前線程會阻塞等待。因此,join
這個名字來源于將子線程與等待它的線程連接在一起的過程。簡單來說,當在瘋狂星期四這天的公司樓下KFC里,在你排隊買快樂時,有你的領導插隊(join進來),你必須等它完成的,所以這也就是等待的本意。
wait()
std::condition_variable::wait()
是一個成員函數,用于阻塞當前線程,直到條件變量被通知。wait()
函數通常與 std::unique_lock<std::mutex>
和 std::mutex
配合使用,以便在等待期間自動解鎖互斥量。可以傳遞一個謂詞函數給 wait()
,以便在條件變量被通知后檢查是否滿足繼續執行的條件。
notify_all()
與notify_one
std::condition_variable::notify_all()
是一個成員函數,用于喚醒所有等待當前條件變量的線程。當某個條件滿足時,可以調用 notify_all()
通知所有等待的線程繼續執行。這是一種線程之間協作的方式。
std::condition_variable::notify_one()
也是一個成員函數,用于喚醒一個正在等待該條件變量的線程。與之相對的,notify_all
是喚醒所有正在等待該條件變量的線程。在某些情況下,您可能只需要喚醒一個等待的線程,而不是所有線程,這時候就可以使用 notify_one
。
再聊聊第三方庫(如:Boost.Thread)方式
#include <iostream> #include <boost/thread.hpp> void print_hello() { std::cout << "Hello from thread!" << std::endl; } int main() { boost::thread thread(print_hello); thread.join(); std::cout << "Hello from main!" << std::endl; return 0; }
最后的戰役:操作系統方式
1.POSIX 線程(pthread):
POSIX 線程是基于 POSIX 標準的一種多線程實現,它在類 Unix 系統(如 Linux、macOS)中廣泛使用。pthread
庫提供了用于創建線程、同步、互斥鎖等多線程功能的函數。然而,由于它是用 C 語言編寫的,所以在 C++ 中使用時可能不夠直觀。以下是一個簡單的使用 POSIX 線程的例子:
#include <iostream> #include <pthread.h> void* print_hello(void* arg) { std::cout << "Hello from thread!" << std::endl; return nullptr; } int main() { pthread_t thread; pthread_create(&thread, nullptr, print_hello, nullptr); pthread_join(thread, nullptr); std::cout << "Hello from main!" << std::endl; return 0; }
2.Windows 線程:
在 Windows 操作系統中,可以通過 Windows API 來創建和管理線程。Windows API 提供了一組用于線程管理、同步和互斥的函數。以下是一個簡單的使用 Windows 線程的例子:
#include <iostream> #include <windows.h> DWORD WINAPI print_hello(LPVOID lpParam) { std::cout << "Hello from thread!" << std::endl; return 0; } int main() { HANDLE thread = CreateThread(nullptr, 0, print_hello, nullptr, 0, nullptr); WaitForSingleObject(thread, INFINITE); CloseHandle(thread); std::cout << "Hello from main!" << std::endl; return 0; }
關于“C++中線程的原理與實現方法是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。