在C++11之前,C++并沒有提供原生的并發支持。開發者通常需要依賴于操作系統的API(如Windows的CreateThread或POSIX的pthread_create)或者第三方庫(如Boost.Thread)來創建和管理線程。這些方式存在以下幾個問題:
-
平臺依賴:不同的操作系統提供了不同的線程API,這意味著你需要為每個目標平臺編寫不同的代碼,或者使用預處理器宏來處理平臺差異。這會使代碼變得復雜和難以維護。
-
錯誤處理困難:操作系統的線程API通常通過返回錯誤碼來報告錯誤,這需要你在每次調用API后檢查錯誤碼,并手動處理錯誤。這不僅繁瑣,而且容易出錯。
-
缺乏高級特性:操作系統的線程API通常只提供了基礎的線程創建和同步功能,缺乏一些高級特性,如線程池、future和promise等。
相比之下,C++11的并發庫提供了以下優勢:
-
平臺無關:C++11的并發庫是C++標準的一部分,這意味著你可以在任何支持C++11的編譯器上使用它,無需考慮平臺差異。
-
異常安全:C++11的并發庫使用異常來報告錯誤,這使得錯誤處理更加簡單和安全。例如,如果你試圖在已經啟動的線程上調用
std::thread::join
,C++11會拋出一個std::system_error
異常。 -
高級特性:C++11的并發庫提供了一些高級特性,如
std::async
、std::future
和std::promise
等,這些特性使得并發編程更加方便和強大。
這些工具使得C++程序員可以更方便、更安全地編寫多線程代碼。下面我們將詳細介紹這些并發工具的使用。
1. 線程(std::thread)
C++11的std::thread
類提供了對操作系統原生線程的封裝。你可以通過創建std::thread
對象來創建新的線程,并通過成員函數join()
或detach()
來等待線程結束或讓線程在后臺運行。
#include <iostream>
#include <thread>void hello() {std::cout << "Hello, concurrent world\n";
}int main() {std::thread t(hello);t.join();
}
在這個例子中,我們創建了一個新的線程來運行hello
函數,并在主線程中通過join()
等待新線程結束。
2. 互斥量(std::mutex)
C++11的std::mutex
類提供了對操作系統原生互斥量的封裝。你可以使用互斥量來保護共享數據,防止多個線程同時訪問。
#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 << '\n';mtx.unlock();
}int main() {std::thread th1(print_block,50,'*');std::thread th2(print_block,50,'$');th1.join();th2.join();return 0;
}
在這個例子中,我們使用互斥量mtx
來保護std::cout
,防止兩個線程同時輸出。
3. 條件變量(std::condition_variable)
C++11的std::condition_variable
類提供了對操作系統原生條件變量的封裝。你可以使用條件變量來實現線程間的同步。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void print_id(int id) {std::unique_lock<std::mutex> lck(mtx);while (!ready) cv.wait(lck);std::cout << "thread " << id << '\n';
}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);std::cout << "10 threads ready to race...\n";go();for (auto& th : threads) th.join();return 0;
}
在這個例子中,我們使用條件變量cv
來實現10個線程的同步。當go
函數被調用時,所有等待在cv
上的線程都會被喚醒。
4. Future(std::future)
C++11的std::future
類提供了一種表示異步操作結果的方式。你可以使用std::async
函數來啟動一個異步操作,并返回一個std::future
對象。然后你可以在任何時候通過std::future::get
函數來獲取異步操作的結果。
#include <iostream>
#include <future>int factorial(int n) {int res = 1;for(int i = n; i > 1; --i)res *= i;return res;
}int main() {std::future<int> fut = std::async(factorial, 5);std::cout << "Factorial of 5 is " << fut.get() << std::endl;return 0;
}
在這個例子中,我們使用std::async
啟動了一個異步操作來計算5的階乘,并通過std::future::get
獲取了結果。