如何理解互斥锁

2024-06-04 19:51:57 浏览数 (1)

互斥锁

使用条件变量的 wait 函数来等待特定条件

代码语言:javascript复制
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>

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

void worker() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []{ return ready; }); // 等待 ready 变量为真
    std::cout << "Worker thread is running" << std::endl;
}

int main() {
    std::thread t(worker);

    {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true; // 设置 ready 变量为真
    }
    cv.notify_one(); // 唤醒等待的线程

    t.join();

    return 0;
}

这个例子中定义了一个互斥锁 mtx,一个条件变量 cv 和一个布尔变量 readyworker 函数是一个线程函数,它在一个独立的线程中运行。

worker 函数中,首先创建了一个 std::unique_lock 对象 lock,用来锁定互斥锁。然后调用条件变量的 wait 函数等待特定条件。wait 函数接受两个参数:一个互斥锁和一个谓词函数。谓词函数用来检查特定条件是否满足。在这个例子中,谓词函数是一个 lambda 表达式,它返回 ready 变量的值。

当调用 wait 函数时,它会自动解锁互斥锁并阻塞当前线程,直到条件变量被唤醒。当条件变量被唤醒时,wait 函数会自动锁定互斥锁,并调用谓词函数检查特定条件是否满足。如果谓词函数返回 true,则表示特定条件已经满足,此时 wait 函数会返回。否则,wait 函数会继续阻塞当前线程,直到条件变量再次被唤醒。

在主函数中,首先创建了一个线程 t,并将其与 worker 函数关联。然后使用花括号创建了一个临时作用域,在该作用域中创建了一个 std::lock_guard 对象 lock,用来锁定互斥锁。接着将 ready 变量设置为 true,表示特定条件已经满足。最后调用条件变量的 notify_one 函数唤醒等待的线程。

需要注意的是,在访问共享变量(如 ready 变量)时,需要使用互斥锁来保护对它的访问。在这个例子中,使用了 std::lock_guard 类来管理互斥锁。当创建一个 std::lock_guard 对象时,它会自动锁定互斥锁;当 std::lock_guard 对象销毁时,它会自动解锁互斥锁。

条件变量的工作原理

可以将条件变量比作一个闹钟,它可以让线程进入睡眠状态,并在特定条件下唤醒线程。当调用条件变量的 wait 函数时,线程就像是一个人躺在床上,按下了闹钟的按钮,然后进入了睡眠状态。此时,线程不会执行任何操作,直到被唤醒。

当其他线程调用条件变量的 notify_onenotify_all 函数时,就相当于闹钟响了。此时,等待的线程会被唤醒,就像人被闹钟吵醒一样。当线程被唤醒后,它会检查特定条件是否满足。如果特定条件已经满足,则线程会继续执行;否则,线程会再次进入睡眠状态,继续等待被唤醒。

例如,在上面的例子中,特定条件是 ready 变量为真。当调用 wait 函数时,线程会进入睡眠状态,并等待 ready 变量变为真。当主函数中将 ready 变量设置为真并调用 notify_one 函数时,等待的线程就会被唤醒。此时,它会检查 ready 变量是否为真。由于 ready 变量已经被设置为真,因此特定条件已经满足,此时 wait 函数会返回。

wait自动解锁互斥锁并阻塞当前线程

可以将互斥锁比作一扇门,它可以防止多个线程同时访问共享资源。当一个线程需要访问共享资源时,它需要先锁定互斥锁,就像用钥匙把门锁上一样。当线程访问完共享资源后,它需要解锁互斥锁,就像用钥匙把门打开一样。

而条件变量可以比作一个闹钟,它可以让线程进入睡眠状态,并在特定条件下唤醒线程。当调用条件变量的 wait 函数时,线程就像是一个人躺在床上,按下了闹钟的按钮,然后进入了睡眠状态。此时,线程不会执行任何操作,直到被唤醒。

当调用 wait 函数时,它会自动解锁互斥锁并阻塞当前线程。这就像一个人躺在床上,按下了闹钟的按钮,然后起身去开门,把门打开,再回到床上继续睡觉。此时,其他线程就可以进入房间(锁定互斥锁)并访问共享资源。

当其他线程调用条件变量的 notify_onenotify_all 函数时,就相当于闹钟响了。此时,等待的线程会被唤醒,就像人被闹钟吵醒一样。当线程被唤醒后,它会起身去关门(锁定互斥锁),然后检查特定条件是否满足。如果特定条件已经满足,则线程会继续执行;否则,线程会再次进入睡眠状态,继续等待被唤醒。

共享资源是房间里的一个东西吗

是的,在这个比喻中,共享资源可以比作房间里的一个东西,例如一张桌子或一个柜子。多个线程可以比作多个人,它们都想进入房间并使用这个共享资源。

由于共享资源只能被一个线程(人)同时访问,因此需要使用互斥锁(门)来防止多个线程(人)同时访问共享资源。当一个线程(人)需要访问共享资源时,它需要先锁定互斥锁(关上门),然后才能访问共享资源。当线程(人)访问完共享资源后,它需要解锁互斥锁(打开门),以便其他线程(人)可以进入房间并访问共享资源。

0 人点赞