C++提供了一些类型的同步机制和线程安全工具。其中一些包括:
-
信号量(Semaphore):信号量是一种更为通用的同步机制,它允许限制同时访问某个资源的线程数量。C++11之后,标准库中引入了std::semaphore,但在此之前,你可以使用操作系统提供的信号量实现。
-
条件变量(Condition Variable):条件变量允许线程在某个条件满足时等待,或者在条件发生变化时被唤醒。条件变量通常与互斥锁一起使用,用于实现复杂的同步逻辑。
-
原子操作(Atomic Operations):原子操作是一种不可中断的操作,能够保证在多线程环境下对共享数据的安全访问。C++标准库提供了std::atomic模板,用于实现原子操作。
-
读写锁(Read-Write Lock):读写锁允许多个线程同时读取共享数据,但只允许一个线程写入共享数据。这可以提高并发性能,因为读取操作不会互斥。
-
条件变量与锁组合的同步工具:在C++标准库中,你可以使用std::condition_variable和std::condition_variable_any与互斥锁一起使用,实现更复杂的同步逻辑。
以上这些工具都是用于实现多线程环境下的线程同步和互斥访问共享资源。在选择合适的同步机制时,需要考虑程序的具体需求、性能和复杂度等因素。
1.互斥锁(Mutex Lock)
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void print_thread_id(int id) {
mtx.lock(); // 加锁
std::cout << "Thread " << id << " is printing.\n";
mtx.unlock(); // 解锁
}
int main() {
std::thread t1(print_thread_id, 1);
std::thread t2(print_thread_id, 2);
t1.join();
t2.join();
return 0;
}
2.信号量(Semaphore)
信号量允许你限制同时访问某个资源的线程数量。以下是一个简单的信号量示例:
#include <iostream>
#include <thread>
#include <semaphore>
const int MAX_THREADS = 2;
std::semaphore sem(MAX_THREADS);
void access_resource(int id) {
sem.acquire(); // 获取信号量
std::cout << "Thread " << id << " is accessing the resource.\n";
// Simulating some work
std::this_thread::sleep_for(std::chrono::seconds(2));
sem.release(); // 释放信号量
}
int main() {
std::thread t1(access_resource, 1);
std::thread t2(access_resource, 2);
std::thread t3(access_resource, 3);
t1.join();
t2.join();
t3.join();
return 0;
}
这个例子中,信号量限制了同时访问资源的线程数量为2,因此第三个线程必须等待其中一个线程释放资源后才能继续执行。
这两个例子展示了互斥锁和信号量的基本用法。其他同步工具的示例也可以按照类似的思路来实现。
3.条件变量(Condition Variable)
条件变量允许线程在某个条件满足时等待,或者在条件发生变化时被唤醒。以下是一个简单的条件变量示例:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id_when_ready(int id) {
std::unique_lock<std::mutex> lck(mtx);
while (!ready) {
cv.wait(lck); // 等待条件变量
}
std::cout << "Thread " << id << " is printing.\n";
}
void set_ready_flag() {
std::this_thread::sleep_for(std::chrono::seconds(2));
{
std::lock_guard<std::mutex> lck(mtx);
ready = true;
}
cv.notify_all(); // 唤醒所有等待线程
}
int main() {
std::thread threads[3];
for (int i = 0; i < 3; ++i) {
threads[i] = std::thread(print_id_when_ready, i + 1);
}
std::thread setter(set_ready_flag);
for (auto& th : threads) {
th.join();
}
setter.join();
return 0;
}
在这个例子中,三个线程等待一个条件(ready标志变为true),其中一个线程在一段时间后设置了这个条件,并通知其他线程。
4.原子操作(Atomic Operations)
原子操作是一种不可中断的操作,用于实现对共享数据的安全访问。以下是一个原子操作的简单示例:
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> counter(0);
void increment_counter(int id) {
for (int i = 0; i < 1000; ++i) {
counter.fetch_add(1); // 原子递增操作
}
}
int main() {
std::thread t1(increment_counter, 1);
std::thread t2(increment_counter, 2);
t1.join();
t2.join();
std::cout << "Counter value: " << counter << std::endl;
return 0;
}
5.读写锁(Atomic Operations)
读写锁允许多个线程同时读取共享数据,但只允许一个线程写入共享数据。这种锁的使用场景是当有大量的读操作和较少的写操作时,使用读写锁可以提高并发性能。
以下是一个简单的读写锁示例:
#include <iostream>
#include <thread>
#include <shared_mutex>
std::shared_mutex rw_mutex;
int shared_data = 0;
void read_data(int id) {
rw_mutex.lock_shared(); // 获取读锁
std::cout << "Thread " << id << " is reading shared data: " << shared_data << std::endl;
rw_mutex.unlock_shared(); // 释放读锁
}
void write_data(int id) {
rw_mutex.lock(); // 获取写锁
shared_data++;
std::cout << "Thread " << id << " is writing shared data: " << shared_data << std::endl;
rw_mutex.unlock(); // 释放写锁
}
int main() {
std::thread readers[3];
std::thread writer;
for (int i = 0; i < 3; ++i) {
readers[i] = std::thread(read_data, i + 1);
}
writer = std::thread(write_data, 4);
for (auto& reader : readers) {
reader.join();
}
writer.join();
return 0;
}