线程同步机制有哪些?
在多线程编程中,多个线程同时访问共享资源时容易引发数据混乱。比如你写一个记账程序,两个线程同时往同一个账户扣钱,结果可能扣少了或多扣了。这时候就得靠线程同步机制来协调,保证操作的有序性。
互斥锁(Mutex)
最常用的同步手段就是互斥锁。它就像一把钥匙,只有拿到钥匙的线程才能进入临界区操作数据,其他线程只能排队等。用完之后要把钥匙交出去,让别人用。
在 C++ 中可以用 std::mutex 实现:
#include <mutex>
std::mutex mtx;
void thread_func() {
mtx.lock();
// 操作共享资源
mtx.unlock();
}实际开发中建议用 lock_guard 这类 RAII 手段,避免忘记解锁。
信号量(Semaphore)
信号量可以控制同时访问某一资源的线程数量。比如你开了个网吧,最多允许10台机器同时上网,那就可以设一个初始值为10的信号量。每进来一个线程就减一,离开就加一。
POSIX 信号量示例:
#include <semaphore.h>
sem_t sem;
sem_init(&sem, 0, 3); // 允许3个线程同时访问
sem_wait(&sem); // P操作
// 访问资源
sem_post(&sem); // V操作条件变量(Condition Variable)
有时候线程需要等某个条件成立才能继续执行。比如生产者往队列放数据,消费者得等队列不为空才能取。这时候光靠锁不行,得配合条件变量。
在 C++ 中常和 mutex 一起使用:
#include <condition_variable>
std::condition_variable cv;
std::mutex cv_m;
bool ready = false;
void waits() {
std::unique_lock<std::mutex> lk(cv_m);
cv.wait(lk, []{ return ready; });
// 条件满足后继续执行
}读写锁(Read-Write Lock)
适用于读多写少的场景。比如配置文件,多个线程可以同时读,但写的时候必须独占。读写锁允许多个读线程并发,写线程则要排他。
pthread 提供了读写锁支持:
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
void read_data() {
pthread_rwlock_rdlock(&rwlock);
// 读操作
pthread_rwlock_unlock(&rwlock);
}
void write_data() {
pthread_rwlock_wrlock(&rwlock);
// 写操作
pthread_rwlock_unlock(&rwlock);
}原子操作(Atomic Operations)
对于简单的变量操作,比如计数器自增,用锁可能有点重。原子操作由硬件直接支持,能保证一条指令完成读-改-写过程,不会被中断。
C++ 中可用 atomic 类型:
#include <atomic>
std::atomic<int> counter(0);
void increment() {
counter++; // 线程安全
}这类操作效率高,但只适合简单场景,复杂逻辑还得靠锁。
这些机制各有适用场合。选对工具,程序才跑得稳又快。日常写代码时别一上来就加锁,先想清楚共享数据怎么访问,再决定用哪种方式同步。