C++ multi-threaded programming allows programs to perform multiple tasks simultaneously, thereby improving performance and responsiveness. C++11 has been introduced<thread>
Library makes multi-threaded programming more convenient. Here are some basic concepts and examples to help you understand how to perform multithreading programming in C++.
1. Create a thread
usestd::thread
The class can create a new thread. You need to pass a function or callable object tostd::thread
Constructor.
#include <iostream> #include <thread> void threadFunction() { std::cout << "Hello from thread!\\\\n"; } int main() { std::thread t(threadFunction); // Create a thread and execute threadFunction (); // Wait for the thread to end std::cout << "Hello from main!\\\\n"; return 0; }
2. Pass parameters to thread function
You can passstd::thread
The constructor passes parameters to the thread function.
#include <iostream> #include <thread> void printMessage(const std::string& message) { std::cout << message << "\\\\n"; } int main() { std::thread t(printMessage, "Hello from thread!"); (); std::cout << "Hello from main!\\\\n"; return 0; }
3. Thread synchronization
Multiple threads may access shared resources at the same time, resulting in data competition. To avoid this, a mutex lock can be used (std::mutex
) to protect shared resources.
#include <iostream> #include <thread> #include <mutex> #include <vector> std::mutex mtx; // Mutex lockvoid printNumber(int num) { (); // Add lock std::cout << "Number: " << num << "\\\\n"; (); // Unlock} int main() { std::vector<std::thread> threads; for (int i = 0; i < 10; ++i) { threads.emplace_back(printNumber, i); } for (auto& t : threads) { (); } return 0; }
4. Use std::lock_guard to automatically manage locks
std::lock_guard
It's a RAII styleSimplelock manager, it automatically locks when constructed and unlocks automatically when destructuring.
#include <iostream> #include <thread> #include <mutex> #include <vector> std::mutex mtx; void printNumber(int num) { std::lock_guard<std::mutex> lock(mtx); // Automatic locking and unlocking std::cout << "Number: " << num << "\\\\n"; } int main() { std::vector<std::thread> threads; for (int i = 0; i < 10; ++i) { threads.emplace_back(printNumber, i); } for (auto& t : threads) { (); } return 0; }
5. Conditional variables
Conditional variables (std::condition_variable
) is used for synchronization between threads, allowing one thread to wait for another thread to meet certain conditions.
Cooperatestd::condition_variable::wait()
The first parameter of the function must be a lock that is more flexible and more complex and severe than lock_guard: std::unique_lock. It can be automatically destructed in RAII or manually lock/unlock, and some code segments in the middle can release the lock. After manually unlocking it, it is just unlocked and not destroyed. You can reuse it again on lock/unlock again as needed.
#include <iostream> #include <thread> #include <mutex> #include <condition_variable> std::mutex mtx; std::condition_variable cv; bool ready = false; void printMessage() { std::**unique_lock**<std::mutex> lock(mtx); (lock, []{ return ready; }); // Wait for the conditions to be met std::cout << "Hello from thread!\\\\n"; } int main() { std::thread t(printMessage); { std::lock_guard<std::mutex> lock(mtx); ready = true; // Set the condition to true } cv.notify_one(); // Notify waiting threads (); std::cout << "Hello from main!\\\\n"; return 0; } compared tolock_guardAdvantages: 1. flexibility:unique_lock Supports delayed locking(You can construct the object first without locking it immediately),and lock_guard Locks must be added during construction。This means you can create it first unique_lock Object,Then call it according to the program logic lock() or unlock() method进行手动加锁or解锁。 2. Support for condition variables:unique_lock Can be used with condition variables in the standard library,like std::condition_variable,This is lock_guard Functions that do not have。This is因为条件变量需要能够原子地释放锁并进入等待状态,This is exactly unique_lock One of the capabilities provided。 3. Transfer of ownership of locks:unique_lock Supports mobile semantics(move semantics),Allows ownership of the lock from one unique_lock Object转移到另一个Object,从and使得锁可以在不同的作用域中传递。and lock_guard This operation is not supported,Its lock ownership is fixed。 4. Try locking(try-locking):Except for the basics lock() and unlock() Outside the method,unique_lock Also provided try_lock() method,该method尝试获取锁但不会阻塞线程,like果无法获得锁则立即返回失败结果。This is very useful for avoiding long thread blockage。 waitThe second parameterpredicateSee the usage of predicates: </w/cpp/thread/condition_variable/wait> predicateThe subsequent statement will not end if it is not satisfied。
6. Thread pool
The C++ standard library does not directly provide the implementation of thread pools, but you can use third-party libraries (such asBoost
) or implement a simple thread pool yourself.
#include <iostream> #include <thread> #include <vector> #include <queue> #include <mutex> #include <condition_variable> #include <functional> class ThreadPool { public: ThreadPool(size_t numThreads) { for (size_t i = 0; i < numThreads; ++i) { workers.emplace_back([this] { while (true) { std::function<void()> task; { std::unique_lock<std::mutex> lock(this->queueMutex); this->(lock, [this] { return this->stop || !this->(); }); if (this->stop && this->()) return; task = std::move(this->()); this->(); } task(); } }); } } template<class F> void enqueue(F&& f) { { std::unique_lock<std::mutex> lock(queueMutex); (std::forward<F>(f)); } condition.notify_one(); } ~ThreadPool() { { std::unique_lock<std::mutex> lock(queueMutex); stop = true; } condition.notify_all(); for (std::thread &worker : workers) { (); } } private: std::vector<std::thread> workers; std::queue<std::function<void()>> tasks; std::mutex queueMutex; std::condition_variable condition; bool stop = false; }; int main() { ThreadPool pool(4); for (int i = 0; i < 8; ++i) { ([i] { std::cout << "Task " << i << " is running on thread " << std::this_thread::get_id() << "\\\\n"; }); } return 0; }
7. Thread local storage
Thread Local Storage (TLS) allows each thread to have its own variable instance. C++11 has been introducedthread_local
Keywords to achieve this.
#include <iostream> #include <thread> thread_local int threadLocalVar = 0; void threadFunction(int id) { threadLocalVar = id; std::cout << "Thread " << id << " has threadLocalVar = " << threadLocalVar << "\\\\n"; } int main() { std::thread t1(threadFunction, 1); std::thread t2(threadFunction, 2); (); (); return 0; }
8. Asynchronous tasks
C++11 also introducedstd::async
andstd::future
, used to perform tasks asynchronously and get results.
#include <iostream> #include <future> int compute() { std::this_thread::sleep_for(std::chrono::seconds(2)); return 42; } int main() { // Start asynchronous task std::future<int> fut = std::async(std::launch::async, compute); // Get results int result = (); std::cout << "Result: " << result << std::endl; return 0; } //Use std::packaged_task (outside the thread function) -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------#include <iostream> #include <future> #include <thread> int compute() { std::this_thread::sleep_for(std::chrono::seconds(2)); return 42; } int main() { // Create packaged_task std::packaged_task<int()> task(compute); // Get future std::future<int> fut = task.get_future(); // Execute tasks in another thread std::thread t(std::move(task)); (); // Get results int result = (); std::cout << "Result: " << result << std::endl; return 0; } // Use std::promise (as a thread function parameter) --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------#include <iostream> #include <future> #include <thread> void compute(std::promise<int> prom) { std::this_thread::sleep_for(std::chrono::seconds(2)); prom.set_value(42); } int main() { // Create promise and future std::promise<int> prom; std::future<int> fut = prom.get_future(); // Execute tasks in another thread std::thread t(compute, std::move(prom)); // Get results int result = (); std::cout << "Result: " << result << std::endl; (); return 0; }
Summary of asynchronous mechanisms: C++11's asynchronous mechanisms (std::future, std::async, std::packaged_task and std::promise) provide the following additional benefits compared to traditional multi-threading programming:
A higher level of abstraction simplifies the management of asynchronous operations.
Automated result delivery and exception handling.
More flexible thread management and task execution strategies.
Clearer code structure and lower coupling.
Supports task combination and timeout waiting.
These mechanisms make asynchronous programming more intuitive, safe and efficient, and are an important part of modern C++ concurrent programming.
Summarize
C++ multithreaded programming provides powerful tools to handle concurrent tasks. By usingstd::thread
、std::mutex
、std::condition_variable
and other tools, you can write efficient and secure multi-threaded programs. It should be noted that multi-threaded programming is prone to introduce problems such as data competition and deadlock, so it requires careful design and testing.
This is the end of this article about multi-threading in the c++ standard library. For more related multi-threading content in the c++ standard library, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!