C++條件變量
C++中的條件變量(Condition Variable)是一種同步原語,用于在多線程程序中阻塞一個或多個線程,直到收到另一個線程的通知。條件變量通常與互斥鎖(Mutex)一起使用,以確保在訪問共享數據時線程之間的同步。
基本概念
- 互斥鎖(Mutex):用于保護共享數據,防止多個線程同時訪問造成數據競爭。
- 條件變量(Condition Variable):用于等待某個條件成立。當條件不滿足時,線程會阻塞在條件變量上,等待其他線程修改條件并通知(notify)條件變量。
使用條件變量的步驟
- 創建互斥鎖和條件變量:在需要同步的共享數據附近創建互斥鎖和條件變量。
- 加鎖:在訪問共享數據或等待條件變量之前,先對互斥鎖加鎖。
- 等待條件變量:如果條件不滿足,則調用條件變量的等待函數(如
wait
、wait_for
、wait_until
),這會使當前線程阻塞并釋放互斥鎖,直到其他線程調用條件變量的通知函數(notify_one
或notify_all
)喚醒它。 - 被喚醒后重新加鎖:當線程被喚醒后,會自動重新對互斥鎖加鎖,然后再次檢查條件是否滿足。
- 解鎖:在訪問完共享數據后,對互斥鎖進行解鎖。
示例
以下是一個簡單的使用條件變量的示例,展示了生產者-消費者模型的基本思想:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>std::mutex mtx;
std::condition_variable cv;
std::queue<int> q;
bool ready = false;void producer(int id) {for (int i = 0; i < 10; ++i) {std::unique_lock<std::mutex> lck(mtx);q.push(i);std::cout << "Produced " << i << " by producer " << id << std::endl;ready = true;cv.notify_one(); // 通知一個等待的線程}
}void consumer() {while (true) {std::unique_lock<std::mutex> lck(mtx);while (!ready) cv.wait(lck); // 等待條件變量// 當我們到達這里時,我們知道條件為真std::cout << "Consumed " << q.front() << std::endl;q.pop();ready = false;if (q.empty()) break; // 如果隊列為空,則退出循環}
}int main() {std::thread producers[10];std::thread consumer_thread(consumer);// 啟動10個生產者for (int i = 0; i < 10; ++i)producers[i] = std::thread(producer, i);// 等待所有生產者完成for (auto& th : producers) th.join();consumer_thread.join();return 0;
}
注意:這個示例為了簡化,ready
變量被用作一個簡單的條件標志。在實際應用中,你可能需要更復雜的條件邏輯來確保線程間的正確同步。此外,示例中的consumer
函數使用了while
循環來檢查條件,這是因為在wait
函數返回后,條件可能已經被其他線程改變了,所以需要重新檢查條件是否仍然滿足。
線程同步和異步
線程同步(Synchronous Threads)和異步(Asynchronous Threads)是并發編程中的兩個重要概念,它們在處理線程間的任務執行順序和數據共享方面存在顯著差異。
線程同步
定義:線程同步是指多個線程在執行過程中,需要按照一定的順序或規則來訪問共享資源或執行特定操作,以確保數據的一致性和程序的正確性。
特點:
- 順序執行:一個線程必須等待另一個線程完成其任務后才能繼續執行。這保證了程序的執行順序和數據的一致性。
- 數據一致性:同步線程能夠確保在多個線程同時訪問共享數據時,數據的一致性和完整性。
- 易于編程和維護:同步線程的執行流程相對簡單,容易理解和實現,也有利于代碼的維護和調試。
- 適用場景:適用于需要按照特定順序執行任務、保證數據一致性的場景,如銀行轉賬系統中的轉賬操作。
實現方式:同步可以通過多種機制來實現,如互斥鎖(Mutex)、信號量(Semaphore)、事件(Event)等。這些機制用于控制線程對共享資源的訪問,確保同一時刻只有一個線程可以訪問該資源。
線程異步
定義:線程異步是指一個線程在執行任務時,不需要等待該任務完成就可以繼續執行其他任務。這種方式允許線程并行處理任務,從而提高程序的執行效率。
特點:
- 并行執行:異步線程允許線程并行處理任務,從而加快程序的執行速度。
- 減少等待時間:當一個線程需要等待某個資源或完成某個長時間運行的任務時,異步線程可以讓其他線程繼續執行,從而減少用戶等待時間。
- 充分利用多核資源:異步線程可以充分利用多核處理器的計算能力,提高程序的并行處理能力。
- 復雜性增加:異步編程通常比同步編程更復雜,需要處理回調、Promise、事件等,容易導致代碼難以理解和維護。
- 調試困難:異步操作的非線性執行順序使得調試和追蹤問題變得更加困難。
適用場景:異步線程適用于需要并行處理大量任務、提高程序執行效率的場景,如網絡編程、文件IO操作、復雜計算等。
C11的同步和異步
在C++中,線程同步和異步是處理多線程程序時需要考慮的重要方面。C++標準庫從C++11開始引入了多線程支持,包括線程(std::thread
)、互斥鎖(std::mutex
)、條件變量(std::condition_variable
)、鎖保護器(如std::lock_guard
和std::unique_lock
)等,用于實現線程的同步和異步。
線程同步
在C++中,線程同步通常涉及使用互斥鎖、條件變量或其他同步機制來確保多個線程在訪問共享資源時不會出現數據競爭或條件競爭。例如:
- 互斥鎖(
std::mutex
):用于保護共享數據,確保同一時間只有一個線程可以訪問該數據。 - 條件變量(
std::condition_variable
):與互斥鎖一起使用,允許線程在特定條件未滿足時掛起,并在條件滿足時被其他線程喚醒。 - 鎖保護器(
std::lock_guard
、std::unique_lock
):這些RAII(Resource Acquisition Is Initialization)風格的封裝器可以自動管理互斥鎖的加鎖和解鎖,減少死鎖的風險。
線程異步
在C++中,實現線程異步通常意味著啟動一個或多個線程來并行執行任務,而這些任務的執行不會阻塞主線程或調用線程。例如:
std::thread
:用于表示一個獨立的執行線程。你可以創建一個std::thread
對象來啟動一個新線程,并在其中執行指定的函數或可調用對象。- 異步操作(如
std::async
):從C++11開始,std::async
函數提供了一種啟動異步任務的方法。它返回一個std::future
對象,該對象可以用來獲取異步操作的結果。std::async
可以自動管理線程的創建和銷毀,以及結果的存儲和檢索。 - 并發算法和容器:C++標準庫還提供了一些并發算法和容器(如
std::vector
的并行算法版本),它們可以在多個線程上并行執行操作,從而提高程序的執行效率。
C++I/o操作
在C++中,I/O(輸入/輸出)操作是程序與外部世界交互的基本方式。C++標準庫提供了豐富的I/O庫來支持文件、控制臺(命令行)以及其他輸入輸出設備的數據交換。以下是一些主要的C++ I/O操作及其相關類的概述:
標準輸入輸出流
std::cin
:用于從標準輸入(通常是鍵盤)讀取數據。std::cout
:用于向標準輸出(通常是屏幕)輸出數據。std::cerr
:用于向標準錯誤輸出流輸出錯誤信息,通常不經過緩沖,直接輸出。std::clog
:類似于std::cerr
,但它會經過緩沖處理。
這些流都繼承自std::ostream
(對于輸出)或std::istream
(對于輸入)類,并提供了豐富的成員函數和操作符重載來支持格式化輸入輸出。
文件輸入輸出
C++通過std::ifstream
(用于從文件讀取數據)、std::ofstream
(用于向文件寫入數據)和std::fstream
(同時支持讀寫)類來支持文件I/O。這些類都是模板類,但它們通常與char
類型一起使用,分別對應std::ifstream<char>
、std::ofstream<char>
和std::fstream<char>
。
#include <fstream>
#include <iostream>int main() {std::ofstream outFile("example.txt");if (outFile.is_open()) {outFile << "Hello, file!";outFile.close();}std::ifstream inFile("example.txt");std::string line;if (inFile.is_open()) {while (getline(inFile, line)) {std::cout << line << '\n';}inFile.close();}return 0;
}
字符串流
C++還提供了std::istringstream
、std::ostringstream
和std::stringstream
類,用于在字符串上進行輸入輸出操作。這些類非常有用,特別是當你需要在內存中進行數據轉換或分割時。
#include <sstream>
#include <iostream>
#include <string>int main() {std::string data = "123 456 789";std::istringstream iss(data);int num;while (iss >> num) {std::cout << num << '\n';}std::ostringstream oss;oss << "The answer is " << 42;std::string answer = oss.str();std::cout << answer << '\n';return 0;
}
格式化輸入輸出
C++標準庫中的I/O流支持通過iomanip
庫中的操作符和函數來格式化輸出。例如,你可以設置浮點數的小數點后的位數、控制整數的基數(十進制、十六進制等)、填充字符等。
#include <iostream>
#include <iomanip>int main() {std::cout << std::fixed << std::setprecision(2) << 3.14159 << '\n'; // 輸出: 3.14std::cout << std::hex << 255 << '\n'; // 輸出: ffreturn 0;
}
緩沖區管理
所有的I/O流都使用緩沖區來管理數據的輸入輸出。你可以通過成員函數如flush()
、sync()
和pubsync()
來管理緩沖區,確保數據被及時寫出或讀入。