当前位置: 首页 > news >正文

C++锁: 读锁,递归锁,超时锁

面试聊到锁,只知道数据库的读锁,不了解编程中的读锁,也没谈,遂败北。

代码分 C++ 和 POSIX 版本

文章目录

      • 互斥锁
      • 读写锁
      • 自旋锁
      • 递归锁
      • 超时锁
      • 原子操作
      • 条件变量
      • 信号量

互斥锁

互斥锁是最基本的线程同步工具,确保同一时刻只有一个线程可以访问共享资源。

如果加锁失败,锁被别人持有,该线程会阻塞,即不占用CPU资源,直至拿到锁。

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

std::mutex mtx;

void worker() {
    // 使用 lock() 方法,若锁被占用则阻塞
    mtx.lock();
    std::cout << "Thread " << std::this_thread::get_id() << " has the lock." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Thread " << std::this_thread::get_id() << " is releasing the lock." << std::endl;
    mtx.unlock();

    // 使用 try_lock() 方法,若锁被占用则返回 false
    if (mtx.try_lock()) {
        std::cout << "Thread " << std::this_thread::get_id() << " acquired the lock using try_lock." << std::endl;
        mtx.unlock();
    } else {
        std::cout << "Thread " << std::this_thread::get_id() << " failed to acquire the lock using try_lock." << std::endl;
    }
}

int main() {
    std::thread t1(worker);
    std::thread t2(worker);

    t1.join();
    t2.join();

    return 0;
}
#include <pthread.h>

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void *thread_func(void *arg) {
    pthread_mutex_lock(&lock);   // 加锁
    // 访问共享资源
    pthread_mutex_unlock(&lock); // 解锁
    return NULL;
}

pthread_mutex_trylock()的方式可以立即返回~

读写锁

读写锁允许多个线程同时读取共享资源,但写操作需要独占。

C++17 是对 shared_mutex 进行 std::shared_lock<> 和 std::unique_ptr<> 这两个lock_guard实现的。

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

std::shared_mutex rwMutex;
int sharedData = 0;

void reader() {
    std::shared_lock<std::shared_mutex> lock(rwMutex);
    std::cout << "Reader reads: " << sharedData << std::endl;
}

void writer() {
    std::unique_lock<std::shared_mutex> lock(rwMutex);
    ++sharedData;
    std::cout << "Writer writes: " << sharedData << std::endl;
}

int main() {
    std::thread r1(reader);
    std::thread w1(writer);

    r1.join();
    w1.join();

    return 0;
}    
#include <pthread.h>

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

void *reader(void *arg) {
    pthread_rwlock_rdlock(&rwlock); // 读锁
    // 读取共享资源
    pthread_rwlock_unlock(&rwlock); // 解锁
    return NULL;
}

void *writer(void *arg) {
    pthread_rwlock_wrlock(&rwlock); // 写锁
    // 修改共享资源
    pthread_rwlock_unlock(&rwlock); // 解锁
    return NULL;
}

自旋锁

线程在获取锁失败时不会进入休眠,而是持续尝试获取锁。
避免了线程上下文切换的开销,适合锁持有时间极短(如几十到几百个 CPU 周期)的场景。多核 CPU 环境且锁竞争不激烈时效果最佳

像互斥锁,会让出CPU,这会交换上下文。最后还要换回来,如果等待时间小于两次交换上下文的时间,那等待就是更高效的。

C++ 模拟 【自旋锁的"忙等待"与"混合模式"的权衡】

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

class SpinLock {
private:
    std::atomic<bool> flag = false;
public:
    void lock() {
        while (flag.exchange(true, std::memory_order_acquire)) {
            while (flag.load(std::memory_order_relaxed)) {
                std::this_thread::yield();
            }
        }
    }

    void unlock() {
        flag.store(false, std::memory_order_release);
    }
};

SpinLock spinLock;
void spinWorker() {
    spinLock.lock();
    std::cout << "Spin lock acquired by thread: " << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    spinLock.unlock();
}

int main() {
    std::thread s1(spinWorker);
    std::thread s2(spinWorker);

    s1.join();
    s2.join();

    return 0;
}    
  • exchange是原子类的替换值操作
    std::memory_order_acquire:这是内存序的一种,用于保证在 exchange 操作之后的所有读写操作不会被重排到 exchange 操作之前,从而确保在获取锁之后对共享资源的访问是安全的。(不会出现先访问共享资源再获取锁的情况,从而确保了对共享资源的安全访问。)【如果学过计算机系统的MIPS,就应该了解流水线,一个道理】

  • load 函数:std::atomic 类的成员函数,用于获取 flag 的当前值。
    std::memory_order_relaxed:这是一种宽松的内存序,只保证原子性,不保证任何顺序性。在这里使用宽松内存序是因为在这个内层循环中,主要目的是快速检查锁的状态,对内存顺序的要求不高,使用宽松内存序可以提高性能。
    std::this_thread::yield() 让出 CPU 时间片

#include <pthread.h>

pthread_spinlock_t spinlock;

void *worker(void *arg) {
    pthread_spin_lock(&spinlock);   // 自旋加锁
    // 访问共享资源
    pthread_spin_unlock(&spinlock); // 解锁
    return NULL;
}

递归锁

初始化的时候设置。
自己调用自己,加同一个锁。

#include <iostream>
#include <thread>
#include <recursive_mutex>

std::recursive_mutex recursiveMutex;

void recursiveWorker(int depth) {
    if (depth == 0) return;
    std::lock_guard<std::recursive_mutex> lock(recursiveMutex);
    std::cout << "Recursive lock acquired at depth " << depth << " by thread: " << std::this_thread::get_id() << std::endl;
    recursiveWorker(depth - 1);
}

int main() {
    std::thread rw(recursiveWorker, 3);
    rw.join();
    return 0;
}    

POSIX 初始化普通锁:

#include <pthread.h>
#include <stdio.h>

pthread_mutex_t lock;

void functionA() {
    pthread_mutex_lock(&lock);
    printf("Function A: Locked\n");

    functionA();  // ✅ 不会死锁,因为递归锁允许同一线程重复加锁

    pthread_mutex_unlock(&lock);
    printf("Function A: Unlocked\n");
}

int main() {
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&lock, &attr);

    functionA();  // 正常执行
    return 0;
}

超时锁

在尝试获取互斥锁时,如果锁没有在指定的 超时时间内 被获取到,线程就会放弃等待并继续执行。

超时(超过 2 秒)仍未获取到锁,它会返回非 0 值

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

std::timed_mutex tmtx;

void worker() {
    // 尝试在 2 秒内获取锁
    if (tmtx.try_lock_for(std::chrono::seconds(2))) {
        std::cout << "Thread " << std::this_thread::get_id() << " acquired the lock using try_lock_for." << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
        tmtx.unlock();
    } else {
        std::cout << "Thread " << std::this_thread::get_id() << " failed to acquire the lock using try_lock_for." << std::endl;
    }
}

int main() {
    std::thread t1(worker);
    std::thread t2(worker);

    t1.join();
    t2.join();

    return 0;
}
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>

pthread_mutex_t lock;

void* threadFunc(void* arg) {
    printf("Thread: Trying to lock...\n");

    struct timespec timeout;
    clock_gettime(CLOCK_REALTIME, &timeout);
    timeout.tv_sec += 2;  // 设置超时时间(2 秒)

    if (pthread_mutex_timedlock(&lock, &timeout) == 0) {
        printf("Thread: Locked!\n");
        pthread_mutex_unlock(&lock);
    } else {
        printf("Thread: Lock timeout! Giving up.\n");
    }

    return NULL;
}

int main() {
    pthread_mutex_init(&lock, NULL);
    
    pthread_mutex_lock(&lock);  // 主线程加锁
    pthread_t thread;
    pthread_create(&thread, NULL, threadFunc, NULL);

    sleep(5);  // 模拟主线程长时间占有锁
    pthread_mutex_unlock(&lock);
    
    pthread_join(thread, NULL);
    return 0;
}

原子操作

一定要谈是操作该变量时

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

std::atomic<int> atomic_counter(0);
void atomic_worker() {
    for (int i = 0; i < 1000; ++i) {
        atomic_counter.fetch_add(1, std::memory_order_relaxed);
    }
}

int main() {
    std::thread a1(atomic_worker);
    std::thread a2(atomic_worker);
    a1.join();
    a2.join();
    std::cout << "Atomic counter value: " << atomic_counter.load() << std::endl;
    return 0;
}    
#include <stdatomic.h>

atomic_int counter = 0;

void *worker(void *arg) {
    atomic_fetch_add(&counter, 1); // 原子递增
    return NULL;
}

条件变量

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

std::mutex cvMutex;
std::condition_variable cv;
bool ready = false;

void waiter() {
    std::unique_lock<std::mutex> lock(cvMutex);
    cv.wait(lock, [] { return ready; });
    std::cout << "Waiter thread is notified." << std::endl;
}

void notifier() {
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    {
        std::lock_guard<std::mutex> lock(cvMutex);
        ready = true;
    }
    cv.notify_one();
    std::cout << "Notifier thread sent notification." << std::endl;
}

int main() {
    std::thread w(waiter);
    std::thread n(notifier);

    w.join();
    n.join();

    return 0;
}    

#include <pthread.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int ready = 0; // 共享状态

void *consumer(void *arg) {
    pthread_mutex_lock(&mutex);
    while (ready == 0) {
        pthread_cond_wait(&cond, &mutex); // 等待条件满足
    }
    // 处理数据
    pthread_mutex_unlock(&mutex);
    return NULL;
}

void *producer(void *arg) {
    pthread_mutex_lock(&mutex);
    ready = 1;
    pthread_cond_signal(&cond); // 唤醒等待线程
    pthread_mutex_unlock(&mutex);
    return NULL;
}

信号量

信号量可以控制多个线程同时访问资源的数量,比如限制访问数据库连接池的线程数。

#include <iostream>
#include <thread>
#include <semaphore>

std::counting_semaphore<1> semaphore(0);

void producer() {
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    std::cout << "Producer is releasing semaphore." << std::endl;
    semaphore.release();
}

void consumer() {
    std::cout << "Consumer is waiting for semaphore." << std::endl;
    semaphore.acquire();
    std::cout << "Consumer acquired semaphore." << std::endl;
}

int main() {
    std::thread p(producer);
    std::thread c(consumer);

    p.join();
    c.join();

    return 0;
}    
#include <semaphore.h>

sem_t sem;

void *worker(void *arg) {
    sem_wait(&sem);  // 获取信号量
    // 访问资源
    sem_post(&sem);  // 释放信号量
    return NULL;
}

int main() {
    sem_init(&sem, 0, 3); // 允许最多3个线程同时访问
    return 0;
}

相关文章:

  • 2025系统分析师---软件工程:深度剖析常见软件开发方法
  • Nature Machine Intelligence 嵌入式大语言模型使机器人能够在不可预测的环境中完成复杂的任务
  • WordPress WooCommerce 本地文件包含漏洞(CVE-2025-1661)
  • 网络编程基础知识——从基础到实操
  • 常见框架漏洞(一)----Thinkphp(TP)
  • Android之卡片式滑动
  • 零基础上手Python数据分析 (9):DataFrame 数据读取与写入 - 让数据自由穿梭
  • 基于Java的班级事务管理系统(源码+lw+部署文档+讲解),源码可白嫖!
  • HarmonyOS-ArkUI Grip组件
  • Charles汉化步骤 charles中文版怎么用
  • 凝视型高光谱相机:钻石光谱分析研究与应用
  • PoE交换机如何助力智慧城市基础设施建设?
  • C# 如何检查给定的四个点是否形成一个正方形(How to check if given four points form a square)
  • docker ssh远程连接
  • uni app跨端开发遇到的问题
  • Linux搭建本地时间服务器及时间同步
  • mysql中show命令的使用
  • react-activation 实现页面保活记录
  • 前端模拟 websocket 请求小工具
  • mac vim命令快捷键
  • 网站图片大小/自动点击器永久免费版
  • 什么自己做网站/怎么做网络推广赚佣金
  • 自己做短视频的网站/网站统计数据分析
  • 有什么做数据的网站/郑州制作网站公司
  • 做网站提高淘宝店排名/南京网络建站公司
  • 东营市城市和建设管理局网站/推广有奖励的app平台