C++11引入了标准库中的多线程支持,包括std::thread类和各种同步机制
互斥锁std::mutex : 互斥锁用于保护共享资源,确保同一时间只有一个线程能够访问该资源,以防止数据竞争。
条件变量std::condition_variable : 条件变量用于线程之间的通信,允许一个或多个线程等待某个条件(或者事件)的发生,而另一个线程则可以通知这些等待的线程该条件已经满足。
原子操作std::atomic : 原子操作用于无锁编程,提供对基本数据类型的原子读写操作,防止数据竞争而无需显式的锁机制。
信号量(Semaphore):用于控制对共享资源的访问,可以实现多个线程之间的同步和互斥。
读写锁(Reader-Writer Lock):用于实现读写线程对共享数据的访问控制,允许多个读线程同时进行读操作,但只允许一个写线程进行写操作。
屏障(Barrier):用于保证多个线程在某个点上同步,只有当所有线程都达到屏障点时才能继续执行。
事件(Event):用于实现线程间的通信和同步,一个线程等待某个事件的发生,而另一个线程触发该事件。
互斥量递归锁(Recursive Mutex):与互斥锁类似,但允许同一个线程多次获得同一个锁,避免死锁。
读写互斥量(Read-Write Mutex):类似于读写锁,但使用互斥量来实现,可以更灵活地控制读写线程对共享数据的访问。
创建和管理线程
cpp
#include <iostream>
#include <thread>
void printMessage(const std::string& message) {
std::cout << message << std::endl;
}
int main() {
std::thread t1(printMessage, "Hello from thread 1");
std::thread t2(printMessage, "Hello from thread 2");
// 等待线程结束
t1.join();
t2.join();
return 0;
}
使用互斥锁进行同步
cpp
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
/* std::lock_guard 是一个模板类,其模板参数是要管理的互斥量的类型。在这里,我们指定了 std::mutex,因为我们要管理 mtx 这个 std::mutex 对象的锁。
lock 是 std::lock_guard 的实例对象名,它通过构造函数接收一个 std::mutex 对象 mtx,并在构造时自动锁定(即调用 mtx.lock())。
当 lock 对象离开其作用域(通常是当前作用域结束或显式销毁),它会自动解锁互斥量(即调用 mtx.unlock()),这样确保了在离开作用域时不会忘记解锁互斥量,避免了潜在的死锁问题。*/
void printMessage(const std::string& message) {
std::lock_guard<std::mutex> lock(mtx);
std::cout << message << std::endl;
}
int main() {
std::thread t1(printMessage, "Hello from thread 1");
std::thread t2(printMessage, "Hello from thread 2");
t1.join();
t2.join();
return 0;
}
使用条件变量进行线程间通信
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
/*std::unique_lock 提供了比 std::lock_guard 更多的灵活性,因为它允许在构造时或之后释放锁。std::condition_variable 允许一个或多个线程等待,直到另一个线程通知它们继续执行。wait 函数会自动释放 lock 并阻塞当前线程,直到满足特定条件。在这里,[] { return ready; } 是一个lambda表达式,它作为条件传递给 wait 函数。条件 ready 是一个全局变量或者类成员变量,用于表示线程是否可以继续执行。一旦条件变量 cv 接收到通知并且lambda表达式返回true(即ready为true),线程就会被唤醒并且重新获得了 lock。然后线程就会输出 "Hello from thread!"*/
void printMessage() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });
std::cout << "Hello from thread!" << std::endl;
}
int main() {
std::thread t(printMessage);
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one(); // 通知一个等待的线程继续执行
t.join(); // 等待线程 t 执行完毕
return 0;
}
信号量(Semaphore):
sem_t semaphore; 这行代码定义了一个信号量变量 semaphore,它是基于 POSIX 标准的信号量类型 sem_t 的实例。
信号量(Semaphore)概述:
信号量是一种用于线程或进程之间同步和互斥的机制。它通常用于控制对共享资源的访问,确保多个线程或进程可以安全地访问共享资源,或者按照特定的顺序执行任务。
POSIX 信号量 sem_t:
在 POSIX 标准中,信号量类型 sem_t 是一种原子类型,可以用于多线程或进程之间的同步。它提供了4个基本的操作:
sem_init:初始化一个信号量。
sem_wait:等待信号量,如果信号量的值大于零,则将其减一;如果为零,则当前线程阻塞等待。
sem_post:发布(释放)信号量,将其值加一,并唤醒一个等待在该信号量上的线程。
sem_destroy:销毁信号量。释放信号量所占用的资源。
信号量常用于以下情况:
线程同步:控制多个线程的执行顺序,确保某些线程在特定条件下执行。
资源管理:限制对共享资源(如文件、内存区域等)的访问,避免数据竞争和冲突。
生产者-消费者问题:协调生产者和消费者线程的操作,确保数据安全和正确处理。
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <semaphore.h>
sem_t semaphore;
/* worker 函数是线程 t 执行的入口函数。
在 worker 函数中,调用 sem_wait(&semaphore) 等待信号量。由于初始时信号量为 0,所以线程会阻塞在这里,直到主线程调用 sem_post(&semaphore) 释放信号量。*/
void worker() {
sem_wait(&semaphore); // 等待信号量
std::cout << "执行任务" << std::endl;
}
int main() {
sem_init(&semaphore, 0, 0); // 通过 sem_init() 函数初始化了一个信号量 semaphore,初始值设为 0。这意味着初始时信号量处于阻塞状态,不允许通过。
std::thread t(worker);
std::this_thread::sleep_for(std::chrono::seconds(2)); // 主线程休眠了 2 秒钟,模拟一些耗时操作或者等待条件满足的过程。
// 主线程通过 sem_post() 函数发送信号量,增加信号量的值。这会解除一个等待在 semaphore 上的线程的阻塞状态。
sem_post(&semaphore); // 发送信号量
// 主线程等待线程 t 执行完毕后再继续执行。线程 t 在执行过程中会调用 worker 函数。
t.join();
// 最后,在程序结束前销毁信号量,释放资源。
sem_destroy(&semaphore);
return 0;
}
读写锁(Reader-Writer Lock):
#include
cpp
#include <thread>
#include <shared_mutex>
// rwMutex 是一个 std::shared_mutex 类型的对象,用于实现读写线程之间的共享访问控制。
// data 是一个整数变量,它是被多个线程共享的数据。
std::shared_mutex rwMutex; // 共享互斥量
int data = 0;
/* reader 函数是一个读者线程的入口函数。它使用 std::shared_lock 对象 lock,这是共享互斥量 rwMutex 的共享模式锁(读锁)。
在 lock 的作用域内,多个读者线程可以同时访问共享资源 data,因为共享锁允许多个线程同时进行读取操作。
reader 函数简单地打印当前的 data 值。*/
void reader() {
std::shared_lock<std::shared_mutex> lock(rwMutex);
std::cout << "读取数据:" << data << std::endl;
}
/*writer 函数是一个写者线程的入口函数。它使用 std::unique_lock 对象 lock,这是共享互斥量 rwMutex 的独占模式锁(写锁)。
在 lock 的作用域内,只有一个写者线程能够修改共享资源 data,因为独占锁会阻止其他线程(包括读者和写者)进入临界区。
writer 函数简单地将 data 值递增,并打印修改后的值。*/
void writer() {
std::unique_lock<std::shared_mutex> lock(rwMutex);
data++;
std::cout << "写入数据:" << data << std::endl;
}
int main() {
std::thread readerThread1(reader);
std::thread readerThread2(reader);
std::thread writerThread(writer);
readerThread1.join();
readerThread2.join();
writerThread.join();
return 0;
}
屏障(Barrier):
cpp
#include <iostream>
#include <thread>
#include <barrier>
std::barrier myBarrier(3); // 创建一个屏障,需要三个线程都到达后才能继续执行
/*worker 函数是每个工作线程的入口函数。
std::this_thread::sleep_for(std::chrono::seconds(1)) 模拟了一些耗时操作,这里是线程休眠1秒钟。
std::cout << "执行任务" << std::endl; 打印了 "执行任务",表示任务执行开始。
myBarrier.arrive_and_wait(); 调用了屏障对象的 arrive_and_wait() 方法,使当前线程等待,直到所有三个线程都到达屏障。
std::cout << "任务完成" << std::endl; 打印了 "任务完成",表示任务执行结束。*/
void worker() {
// 模拟一些操作
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "执行任务" << std::endl;
myBarrier.arrive_and_wait(); // 到达屏障并等待
std::cout << "任务完成" << std::endl;
}
int main() {
std::thread t1(worker);
std::thread t2(worker);
std::thread t3(worker);
t1.join();
t2.join();
t3.join();
return 0;
}
<< 执行任务
执行任务
执行任务
任务完成
任务完成
任务完成
事件(Event)
事件可以用于实现线程间的通信和同步,一个线程等待某个事件的发生,而另一个线程触发该事件。
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool eventOccurred = false;
/*std::unique_lock<std::mutex> lock(mtx);:使用 unique_lock 在互斥量上进行加锁。
cv.wait(lock);:在等待期间,线程会释放锁,并且会阻塞直到收到 cv.notify_one() 通知。
一旦事件发生 (eventOccurred 变为 true),线程被唤醒,输出消息并执行任务。*/
void waitForEvent() {
std::unique_lock<std::mutex> lock(mtx);
while (!eventOccurred) {
cv.wait(lock);
}
std::cout << "事件已发生,执行任务" << std::endl;
}
/*std::lock_guard<std::mutex> lock(mtx);:使用 lock_guard 对互斥量进行短期锁定,确保修改 eventOccurred 的操作是原子的。
cv.notify_one();:通知一个正在等待的线程(如果有的话),以便它可以继续执行。
输出 "事件已触发" 的消息。*/
void triggerEvent() {
{
std::lock_guard<std::mutex> lock(mtx);
eventOccurred = true;
}
cv.notify_one();
std::cout << "事件已触发" << std::endl;
}
int main() {
std::thread t1(waitForEvent);
std::this_thread::sleep_for(std::chrono::seconds(2));
std::thread t2(triggerEvent);
t1.join();
t2.join();
return 0;
}
互斥量递归锁(Recursive Mutex)
互斥量递归锁允许同一个线程多次获得同一个锁,避免了死锁。
cpp
#include <iostream>
#include <thread>
#include <mutex>
std::recursive_mutex mtx;
/*std::lock_guard<std::recursive_mutex> lock(mtx);:在函数内部使用 lock_guard 对递归互斥锁 mtx 进行加锁。因为是递归互斥锁,同一线程可以多次加锁而不会产生死锁。
if (depth > 0):递归终止条件,如果递归深度大于0,则输出当前深度并继续递归调用 recursiveFunction。*/
void recursiveFunction(int depth) {
std::lock_guard<std::recursive_mutex> lock(mtx);
if (depth > 0) {
std::cout << "递归深度:" << depth << std::endl;
recursiveFunction(depth - 1);
}
}
int main() {
recursiveFunction(3);
return 0;
}
读写互斥量(Read-Write Mutex)
读写互斥量使用互斥量来实现,可以更灵活地控制读写线程对共享数据的访问。
cpp
#include <iostream>
#include <thread>
#include <mutex>
std::mutex readMutex;
std::mutex writeMutex;
int data = 0;
void reader() {
std::lock_guard<std::mutex> lock(readMutex);
std::cout << "读取数据:" << data << std::endl;
}
void writer() {
std::lock_guard<std::mutex> lock(writeMutex);
data++;
std::cout << "写入数据:" << data << std::endl;
}
int main() {
std::thread readerThread1(reader);
std::thread readerThread2(reader);
std::thread writerThread(writer);
readerThread1.join();
readerThread2.join();
writerThread.join();
return 0;
}