在 C++ 中,实现线程同步主要有以下几种常见方法

  1. 互斥锁(Mutex)
    原理:
    互斥锁用于保护共享资源,确保在同一时刻只有一个线程能够访问被保护的资源。当一个线程想要访问共享资源时,它首先尝试获取互斥锁。如果锁已经被其他线程持有,那么该线程会被阻塞,直到锁被释放。一旦获取到锁,线程就可以安全地访问共享资源,访问结束后再释放锁,以便其他线程能够获取锁并访问资源。
    示例代码:
    cpp
    复制
#include <iostream>
#include <thread>
#include <mutex>

// 定义一个互斥锁
std::mutex mutex_f;

// 共享资源,这里是一个简单的整数变量
int shared_resource = 0;

// 线程函数,用于对共享资源进行操作
void increment_resource() {
    // 获取互斥锁
    std::lock_guard<std::mutex> guard(mutex_f);

    // 对共享资源进行操作
    shared_resource++;

    // 释放互斥锁(由lock_guard的析构函数自动完成)
}

int main() {
    // 创建两个线程
    std::thread t1(increment_resource);
    std::thread t2(increment_resource);

    // 等待线程完成
    t1.join();
    t2.join();

    // 输出共享资源的值
    std::cout << "Shared resource value: " << shared_resource << std::endl;

    return 0;
}

在上述示例中,通过std::lock_guard来自动管理互斥锁的获取和释放,确保在increment_resource函数中对共享资源shared_resource的操作是线程安全的。
2. 条件变量(Condition Variable)
原理:
条件变量通常与互斥锁一起使用,用于让一个线程等待某个特定条件的满足。线程首先获取互斥锁,然后检查条件是否满足。如果条件不满足,线程会释放互斥锁并进入等待状态,直到被其他线程通过通知机制唤醒。被唤醒后,线程会再次获取互斥锁并重新检查条件是否满足,只有当条件满足时才会继续执行后续操作。
示例代码:
cpp
复制

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

// 定义互斥锁和条件变量
std::mutex mutex_g;
std::condition_variable condition_g;

// 共享资源,这里是一个简单的整数变量
int shared_resource_g = 0;

// 用于标识条件是否满足的标志
bool condition_met = false;

// 线程函数,用于增加共享资源的值并在满足条件时通知其他线程
void increment_and_notify() {
    // 获取互斥锁
    std::lock_guard<std::mutex> guard(mutex_g);

    // 对共享资源进行操作
    shared_resource_g++;

    // 当共享资源的值达到一定程度时,设置条件满足标志并通知其他线程
    if (shared_resource_g >= 5) {
        condition_met = true;
        condition_g.notify_all();
    }
}

// 线程函数,用于等待条件满足
void wait_for_condition() {
    // 获取互斥锁
    std::unique_lock<std::mutex> lock(mutex_g);

    // 等待条件满足,当条件不满足时释放互斥锁并进入等待状态
    condition_g.wait(lock, []{ return condition_met; });

    // 条件满足后,输出相关信息
    std::cout << "Condition met, shared resource value: " << shared_resource_g << std::endl;
}

int main() {
    // 创建两个线程
    std::thread t1(increment_and_notify);
    std::thread t2(wait_for_condition);

    // 等待线程完成
    t1.join();
    t2.join();

    return 0;
}

在上述示例中,increment_and_notify线程负责增加共享资源的值,并在满足特定条件(shared_resource_g >= 5)时通知其他线程。wait_for_condition线程则通过条件变量等待条件满足,在条件满足后输出相关信息。
3. 信号量(Semaphore)
原理:
信号量是一种用于控制对共享资源访问的计数器。它有一个初始值,表示可用资源的数量。当一个线程想要访问共享资源时,它会先尝试获取信号量。如果信号量的值大于 0,线程就可以获取信号量(即将信号量的值减 1)并访问共享资源。访问结束后,线程会释放信号量(即将信号量的值加 1),以便其他线程能够获取信号量并访问资源。如果信号量的值为 0,线程会被阻塞,直到有其他线程释放信号量。
示例代码:
cpp
复制

#include <iostream>
#include <thread>
#include <semaphore.h>

// 定义一个信号量,并设置初始值为3,表示有3个可用资源
sem_t semaphore_h;

// 共享资源,这里简单地用一个整数变量表示
int shared_resource_h = 0;

// 线程函数,用于获取信号量、访问共享资源并释放信号量
void access_shared_resource() {
    // 获取信号量
    sem_wait(&semaphore_h);

    // 对共享资源进行操作
    shared_resource_h++;

    // 释放信号量
    sem_post(&semaphore_h);

    // 输出相关信息
    std::cout << "Accessed shared resource, current value: " << shared_resource_h << std::endl;
}

int main() {
    // 初始化信号量
    sem_init(&semaphore_h, 0, 3);

    // 创建三个线程
    std::thread t1(access_shared_resource);
    std::thread t2(access_shared_resource);
    std::thread t3(access_shared_resource);

    // 等待线程完成
    t1.join();
    t2.join();
    t3.join();

    // 销毁信号量
    sem_destroy(&semaphore_h);

    return 0;
}

在上述示例中,通过sem_wait获取信号量,sem_post释放信号量,实现了对共享资源shared_resource_h的线程安全访问。
4. 读写锁(Read-Write Lock)
原理:
读写锁用于区分对共享资源的读操作和写操作。多个线程可以同时进行读操作,因为读操作不会改变共享资源的状态。但是,当有一个线程进行写操作时,其他线程无论是读还是写都不能同时进行,必须等待写操作完成。读写锁通过维护读锁和写锁的状态来实现这种区分。
示例代码:
cpp
复制

#include <iostream>
#include <thread>
#include <shared_mutex>

// 定义一个读写锁
std::shared_mutex read_write_lock_i;

// 共享资源,这里是一个简单的整数变量
int shared_resource_i = 0;

// 线程函数,用于进行读操作
void read_shared_resource() {
    // 获取读锁
    std::shared_lock<std::shared_mutex> read_lock(read_write_lock_i);

    // 对共享资源进行读操作
    std::cout << "Read shared resource value: " << shared_resource_i << std::endl;

    // 释放读锁(由shared_lock的析构函数自动完成)
}

// 线程函数,用于进行写操作
void write_shared_resource() {
    // 获取写锁
    std::unique_lock<std::shared_mutex> write_lock(read_write_lock_i);

    // 对共享资源进行写操作
    shared_resource_i++;

    // 释放写锁(由unique_lock的析构函数自动完成)
}

int main() {
    // 创建三个线程进行读操作,一个线程进行写操作
    std::thread t1(read_shared_resource);
    std::thread t2(read_shared_resource);
    std::thread t3(read_shared_resource);
    std::thread t4(write_shared_resource);

    // 等待线程完成
    t1.join();
    t2.join();
    t3.join();
    t4.join();

    return 0;
}

在上述示例中,通过std::shared_lock获取读锁进行读操作,通过std::unique_lock获取写锁进行写操作,实现了对共享资源shared_resource_i的读写操作的线程安全管理。
5. 原子类型(Atomic Types)
原理:
原子类型是 C++11 及以后引入的一种数据类型,它允许对其进行原子操作,即这些操作在多线程环境下是不可分割的,要么完整执行,要么不执行,不存在中间状态被其他线程看到的情况。常见的原子类型有std::atomic、std::atomic等。通过使用原子类型来定义共享资源,可以避免使用传统的同步机制(如互斥锁等)来实现线程安全。
示例代码:
cpp
复制

#include <iostream>
#include <thread>
#include <atomic>

// 定义一个原子类型的整数变量
std::atomic<int> atomic_shared_resource_j(0);

// 线程函数,用于对原子类型的共享资源进行操作
void increment_atomic_resource() {
    // 对原子类型的共享资源进行操作
    atomic_shared_resource_j++;

    // 输出相关信息
    std::cout << "Atomic shared resource value: " << atomic_shared_resource_j.load() << std::endl;
}

int main() {
    // 创建两个线程
    std::thread t1(increment_atomic_resource);
    std::thread t2(increment_atomic_resource);

    // 等待线程完成
    t1.join();
    t2.join();

    return 0;
}

在上述示例中,通过使用std::atomic定义共享资源atomic_shared_resource_j,并直接对其进行原子操作(如++操作),实现了线程安全的资源管理。

posted @ 2024-11-09 18:10  MarsCactus  阅读(56)  评论(0编辑  收藏  举报