C++11引入了許多新的特性,其中包括對多線程編程的支持。本文將介紹C++11中多線程編程的各種方面,包括並發編程、異步任務、原子操作、鎖機制以及線程池等。
一、並發編程
並發編程是指在一個應用程序中同時執行多個任務的能力。多線程是實現並發編程的一種常見方式。C++11為多線程編程提供了一組重要的工具,包括線程、互斥鎖、條件變量、原子操作和同步隊列。下面分別介紹這些內容。
1. 線程
線程是指進程中執行的並發任務。C++11中的線程類是std::thread,使用它可以創建和控制線程。下面是一個簡單的線程示例,展示了如何創建和啟動一個新的線程。
#include #include void thread_function() { std::cout << "thread function running" << std::endl; } int main() { std::thread t(&thread_function); std::cout << "main thread running" << std::endl; t.join(); return 0; }
在上面的示例中,std::thread對象t是一個新線程,它的構造函數接受線程函數和函數參數。在這個示例中,線程函數是thread_function(),沒有參數。線程對象t被創建後,可以調用它的join()方法來等待線程完成。
2. 互斥鎖和條件變量
在多線程程序中,對共享數據的並發訪問可能會導致問題。為了避免這些問題,C++11提供了互斥鎖和條件變量。互斥鎖可以用於同步多個線程之間的訪問,條件變量可以用於在線程之間傳遞信號。下面是使用互斥鎖和條件變量的示例:
#include #include #include #include #include std::mutex mtx; std::condition_variable cv; std::queue q; bool ready = false; void producer_thread() { for (int i = 0; i < 5; ++i) { std::unique_lock lock(mtx); q.push(i); std::cout << "producer thread pushed " << i << std::endl; ready = true; cv.notify_one(); } } void consumer_thread() { while (true) { std::unique_lock lock(mtx); while (!ready) cv.wait(lock); if (q.empty()) break; int data = q.front(); q.pop(); std::cout << "consumer thread got " << data << std::endl; ready = false; } } int main() { std::thread producer(producer_thread); std::thread consumer(consumer_thread); producer.join(); consumer.join(); return 0; }
在上面的示例中,我們使用了互斥鎖和條件變量來同步生產者和消費者線程。生產者線程調用cv.notify_one()來通知消費者線程可以取數據了,消費者線程使用cv.wait(lock)來等待生產者線程的通知。
二、異步任務
異步任務是指程序中可以單獨執行、不阻塞主線程的任務。C++11為異步任務提供了一個很方便的方式:std::async()。使用std::async()可以在新的線程中異步執行一個函數,同時返回一個std::future對象,該對象可以用於獲取函數的返回值或者等待函數執行完成。下面是std::async()的一個示例:
#include #include int async_function() { std::cout << "async_function running" << std::endl; return 42; } int main() { std::future f = std::async(std::launch::async, &async_function); std::cout << "main thread running" << std::endl; std::cout << "async_function returned " << f.get() << std::endl; return 0; }
在上面的示例中,std::async()函數被用來異步執行函數async_function()。該函數返回一個std::future對象f,用於獲取異步函數的返回值。在主函數中,我們使用f.get()方法來等待異步函數執行完成並且獲取其返回值。
三、原子操作和鎖機制
原子操作是指可以在沒有鎖的情況下對共享數據進行讀寫操作的語句。C++11中提供了std::atomic類來支持原子操作。std::atomic類可以確保任何對原子對象的操作都是對整個值進行原子化的,因此不會發生競爭條件。下面是一個原子操作的示例:
#include #include #include std::atomic counter(0); void thread_function() { for (int i = 0; i < 10000; ++i) { counter++; } } int main() { std::thread t1(thread_function); std::thread t2(thread_function); t1.join(); t2.join(); std::cout << "counter = " << counter << std::endl; return 0; }
上面的示例中,我們使用std::atomic來定義了一個原子化的計數器。在兩個線程中分別對其進行了10000次自增操作。由於使用了std::atomic,所以兩個線程對計數器的自增操作是原子化的,不會導致數據競爭。最終輸出的counter的值為20000。
鎖機制也是避免多線程訪問共享數據時出現數據競爭的一種重要手段。C++11提供了多種類型的鎖來支持多線程程序。下面是使用std::mutex進行線程同步的示例:
#include #include #include std::mutex mtx; int counter = 0; void thread_function() { for (int i = 0; i < 10000; ++i) { std::unique_lock lock(mtx); counter++; } } int main() { std::thread t1(thread_function); std::thread t2(thread_function); t1.join(); t2.join(); std::cout << "counter = " << counter << std::endl; return 0; }
在上面的示例中,我們使用std::mutex來定義了一個互斥鎖。在兩個線程中對計數器進行自增前,先進行了加鎖操作,以保證操作的原子性和線程安全。最終計數器的值也是20000。
四、線程池
線程池是一種常見的並發編程技術,通過維護一組線程,以待執行的任務隊列作為輸入,執行任務隊列中的任務,以實現多線程並發執行。C++11中並沒有提供線程池的實現,但我們可以自己實現一個簡單的線程池。下面是一個線程池的示例:
#include #include #include #include #include class ThreadPool { public: ThreadPool(std::size_t n) : stopped(false) { for (std::size_t i = 0; i < n; ++i) { workers.emplace_back( [this] { for (;;) { std::function task; { std::unique_lock lock(queue_mutex); condition.wait(lock, [this]() { return stopped || !tasks.empty(); }); if (stopped && tasks.empty()) return; task = std::move(tasks.front()); tasks.pop(); } task(); } } ); } } template auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of::type> { using return_type = typename std::result_of::type; auto task = std::make_shared<std::packaged_task>(std::bind(std::forward(f), std::forward(args)...)); std::future res = task->get_future(); { std::unique_lock lock(queue_mutex); if (stopped) throw std::runtime_error("enqueue on stopped ThreadPool"); tasks.emplace([task]() { (*task)(); }); } condition.notify_one(); return res; } ~ThreadPool() { { std::unique_lock lock(queue_mutex); stopped = true; } condition.notify_all(); for (std::thread& worker : workers) worker.join(); } private: std::vector workers; std::queue<std::function> tasks; std::mutex queue_mutex; std::condition_variable condition; bool stopped; }; int main() { ThreadPool pool(4); std::vector<std::future> results; for (int i = 0; i int { std::cout << "task " << i << " running" << std::endl; return i * i; }, i) ); } for (std::future& result : results) { std::cout << "result = " << result.get() << std::endl; } return 0; }
在上面的示例中,我們定義了一個ThreadPool類,它維護了一組線程和一個任務隊列。線程每次從任務隊列中取出一個任務執行,直到任務隊列為空或者被告知停止。ThreadPool類提供了enqueue()函數來向任務隊列中添加任務,enqueue()函數返回一個std::future對象,用於獲取任務的返回值。
結論
本文介紹了C++11中多線程編程的各種方面,包括並發編程、異步任務、原子操作、鎖機制以及線程池等。多線程編程是一項複雜的任務,需要仔細考慮應用程序的數據共享和線程同步問題。通過了解C++11多線程編程提供的工具和技術,可以使多線程編程更加容易和高效。
原創文章,作者:DISNI,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/329672.html