C++中关于同步锁的了解

156 阅读5分钟

在C++中,锁的原理涉及到操作系统底层的线程调度和同步机制。锁的基本原理是通过互斥量(mutex)来实现线程之间的同步,确保在任意时刻只有一个线程可以访问临界区(共享资源)。下面是锁的基本原理:

  1. 互斥量(Mutex) : 互斥量是一种同步原语,用于保护共享资源。它可以是硬件提供的,也可以是操作系统提供的软件实现。互斥量有两种状态:锁定(locked)和未锁定(unlocked)。当一个线程成功地锁定了互斥量后,其他线程试图锁定该互斥量时会被阻塞,直到该互斥量被解锁。
  2. 锁定(Locking) : 当一个线程希望访问共享资源时,它会尝试锁定相应的互斥量。如果互斥量当前处于未锁定状态,那么该线程可以成功地锁定互斥量,然后访问临界区。如果互斥量已经被其他线程锁定,则当前线程会被阻塞,直到互斥量被解锁。
  3. 解锁(Unlocking) : 当一个线程完成了对临界区的访问后,它会解锁相应的互斥量,这样其他线程就可以继续访问共享资源了。
  4. 阻塞和唤醒: 当一个线程在尝试锁定互斥量时被阻塞时,它会进入睡眠状态,直到互斥量被解锁,或者在超时情况下唤醒。唤醒可以由解锁操作、其他线程的信号、定时器等方式触发。
  5. 竞争条件(Race Condition)的避免: 锁的原理在于避免多个线程同时访问临界区,从而防止竞争条件的发生。竞争条件可能导致数据不一致或不可预测的行为,因此锁是实现多线程程序正确性的重要手段。

总的来说,锁的原理在于提供一种机制,确保对共享资源的访问是互斥的,同时通过阻塞和唤醒机制来实现线程的同步,从而避免竞争条件的发生。

在C++中,同步锁用于实现多线程环境下的线程同步,以确保对共享资源的访问是安全的。以下是C++中常见的同步锁:

  1. std::mutexstd::mutex 是C++标准库提供的最基本的互斥量。它提供了最简单的锁定和解锁操作。当一个线程锁定了 std::mutex 后,其他线程如果尝试再次锁定同一个 std::mutex,则会被阻塞,直到锁定的线程解锁了 std::mutex
  2. std::recursive_mutexstd::recursive_mutexstd::mutex 的变种,它允许同一线程多次锁定同一个互斥量而不会死锁。但要注意,每次锁定都必须有相应的解锁,否则可能导致死锁。
  3. std::timed_mutexstd::timed_mutex 是带有超时功能的互斥量。除了提供常规的锁定和解锁操作外,它还提供了 try_lock_for()try_lock_until() 方法,允许在一段时间内尝试获取锁,并在超时时返回。
  4. std::recursive_timed_mutexstd::recursive_timed_mutexstd::recursive_mutex 的带有超时功能的变种。
  5. std::shared_mutex (C++17): std::shared_mutex 是C++17标准引入的共享互斥量。它允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。这种锁通常用于读者-写者问题。
  6. std::condition_variablestd::condition_variable 是C++标准库提供的条件变量,用于在多线程间进行通信。它通常与互斥量一起使用,允许一个线程等待某个条件的发生,并在条件满足时通知其他线程。
  7. std::atomicstd::atomic 是C++11标准引入的原子类型,用于对共享变量进行原子操作。它提供了一系列的原子操作函数,如 load()store()exchange()compare_exchange_weak()compare_exchange_strong() 等。

这些同步锁提供了不同的特性和适用场景,可以根据具体的需求选择合适的同步机制。

下面是它们的简单介绍以及使用示例:

  1. std::mutex
cppCopy code
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;

void print_thread_id(int id) {
    mtx.lock();
    std::cout << "Thread " << id << " is working" << std::endl;
    mtx.unlock();
}

int main() {
    std::thread t1(print_thread_id, 1);
    std::thread t2(print_thread_id, 2);

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

    return 0;
}
  1. std::recursive_mutex
cppCopy code
#include <iostream>
#include <thread>
#include <mutex>

std::recursive_mutex rmtx;

void print_recursive_thread_id(int id, int depth) {
    if (depth <= 0) return;

    rmtx.lock();
    std::cout << "Thread " << id << " is at depth " << depth << std::endl;
    print_recursive_thread_id(id, depth - 1);
    rmtx.unlock();
}

int main() {
    std::thread t1(print_recursive_thread_id, 1, 3);

    t1.join();

    return 0;
}
  1. std::timed_mutex
cppCopy code
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

std::timed_mutex tmtx;

void try_lock_for_example(int id, int milliseconds) {
    if (tmtx.try_lock_for(std::chrono::milliseconds(milliseconds))) {
        std::cout << "Thread " << id << " locked mutex for " << milliseconds << " milliseconds" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // Simulate work
        tmtx.unlock();
    } else {
        std::cout << "Thread " << id << " failed to lock mutex" << std::endl;
    }
}

int main() {
    std::thread t1(try_lock_for_example, 1, 2000);
    std::thread t2(try_lock_for_example, 2, 1000);

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

    return 0;
}
  1. std::recursive_timed_mutex:使用方式与 std::timed_mutex 相似,只是 std::recursive_timed_mutex 允许同一线程多次获取锁。
  2. std::shared_mutex (C++17):用于实现读写锁:
cppCopy code
#include <iostream>
#include <thread>
#include <shared_mutex>

std::shared_mutex smtx;

void read_data(int id) {
    smtx.lock_shared();
    std::cout << "Thread " << id << " is reading data" << std::endl;
    smtx.unlock_shared();
}

void write_data(int id) {
    smtx.lock();
    std::cout << "Thread " << id << " is writing data" << std::endl;
    smtx.unlock();
}

int main() {
    std::thread t1(read_data, 1);
    std::thread t2(write_data, 2);
    std::thread t3(read_data, 3);

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

    return 0;
}

这些示例演示了如何使用不同类型的同步锁来确保线程间的安全访问。