详解C++11中的线程锁和条件变量

详解C++11中的线程锁和条件变量

C++11中提供了一系列的线程同步机制,包括线程锁和条件变量。线程锁主要是为了保护共享资源,防止多个线程同时对同一块内存区域进行操作而发生冲突;而条件变量则主要是为了线程之间的协作,当一个线程等待某个条件成立时,可以通过条件变量来阻塞当前线程,直到条件被满足为止。

线程锁

Mutex

Mutex(互斥锁)是最基本的线程锁,它可以确保同一时刻只有一个线程能够访问被保护的共享资源,从而避免竞争条件的发生。C++11中提供了多种Mutex,包括std::mutex、std::recursive_mutex、std::timed_mutex等等。以std::mutex为例,下面是如何使用Mutex进行线程同步的示例代码:

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

std::mutex mutex;

void print(int i) {
    mutex.lock();
    std::cout << "Thread " << i << " is running" << std::endl;
    mutex.unlock();
}

int main() {
    std::thread threads[5];
    for(int i = 0; i < 5; i++) {
        threads[i] = std::thread(print, i);
    }
    for(std::thread& t : threads) {
        t.join();
    }
    return 0;
}

上面的代码中,我们首先创建了一个std::mutex对象,然后在print函数中对其加锁,确保同一时刻只有一个线程可以输出信息,输出完成后再进行解锁操作。在主函数中创建了5个线程,并将它们执行print函数。最后使用join等待所有线程执行结束。需要注意的是,如果Mutex没有被正确释放,可能会导致死锁或者程序卡住。

RecursiveMutex

RecursiveMutex又称为可重入锁,是一种支持同一线程多次获取锁的锁类型。如果某个线程已经获取了RecursiveMutex并且没有释放,则可以再次获取该Mutex,同时需要确保每次获取和释放Mutex的次数要一样,不然会导致死锁。以下是使用RecursiveMutex的示例代码:

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

int count = 0;
std::recursive_mutex mutex;

void increment(int i) {
    mutex.lock();
    std::cout << "thread " << i << " is incrementing count" << std::endl;
    ++count;
    if(count < 10) {
        increment(i);
    }
    mutex.unlock();
}

int main() {
    std::thread threads[5];
    for(int i = 0; i < 5; i++) {
        threads[i] = std::thread(increment, i);
    }
    for(std::thread& t : threads) {
        t.join();
    }
    std::cout << "final count = " << count << std::endl;
    return 0;
}

上面的代码中,我们首先定义了一个std::recursive_mutex和一个计数器count。在increment函数中,每个线程会加锁并且对count进行加1操作,如果count小于10,则递归调用increment函数。需要注意的是,在每次递归调用时,都会再次获取Mutex,如果Mutex没有被正确释放,可能会导致死锁。

条件变量

条件变量是用于线程之间同步的重要机制,它可以实现线程之间的协作,使得某个线程等待某个特定条件满足后才继续执行。C++11中提供了std::condition_variable和std::condition_variable_any两种条件变量实现方式,前者只支持std::unique_lock并且需要和std::mutex一起使用,后者则可以与任何符合要求的锁类型一起使用。以下是使用std::condition_variable的示例代码:

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

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

void worker() {
    std::unique_lock<std::mutex> lock(mutex);
    while(!ready) {
        cv.wait(lock);
    }
    std::cout << "worker thread is running" << std::endl;
}

void notifier() {
    std::lock_guard<std::mutex> lock(mutex);
    ready = true;
    cv.notify_one();
}

int main() {
    std::thread t1(worker);
    std::thread t2(notifier);
    t1.join();
    t2.join();
    return 0;
}

上面的代码中,我们首先定义了一个Mutex和一个条件变量cv,再定义了一个标记变量ready,用于标记是否满足某个条件。在worker函数中,我们首先对Mutex进行加锁,然后进入while循环等待条件变量满足,如果条件变量没有满足,则会调用cv.wait()函数阻塞当前线程,同时会释放Mutex,直到收到notify_one通知后,才会继续执行。而在notifier函数中,我们首先对Mutex进行lock_guard加锁,然后将ready标记为true,并调用cv.notify_one()函数通知所有正在等待的线程。

在实际使用中,我们可以使用条件变量来实现多个线程之间的同步协作,例如生产者消费者模型、优先级队列等。以下是使用条件变量实现一个简单的生产者消费者模型的示例代码:

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

std::queue<int> q;
std::mutex mutex;
std::condition_variable cv;

void producer() {
    for(int i = 0; i < 10; i++) {
        std::lock_guard<std::mutex> lock(mutex);
        q.push(i);
        std::cout << "produced " << i << std::endl;
        cv.notify_one();
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }
}

void consumer() {
    while(true) {
        std::unique_lock<std::mutex> lock(mutex);
        cv.wait(lock, [](){return !q.empty();});
        int value = q.front();
        q.pop();
        std::cout << "consumed " << value << std::endl;
    }
}

int main() {
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}

上面的代码中,我们定义了一个队列q、一个Mutex和一个条件变量cv,同时定义了一个producer函数和一个consumer函数。在producer函数中,我们依次向队列中添加元素,并且通过条件变量cv通知正在等待的线程可以进行消费了。在consumer函数中,我们使用while循环不断地从队列中获取元素,如果队列中没有元素,就会进入阻塞状态,直到通过cv.wait()函数收到生产者的通知后,才会继续执行。

需要注意的是,在使用条件变量的时候,我们通常需要使用std::unique_lock来对Mutex进行加锁解锁操作,同时在调用cv.wait()函数的时候,可以通过lambda表达式来判断条件是否满足。

总结

在本篇攻略中,我们详细介绍了C++11中的线程锁和条件变量机制,包括Mutex、RecursiveMutex、std::condition_variable和std::condition_variable_any,在讲解的过程中,也给出了对应的示例代码。线程同步是多线程编程中非常重要的一环,掌握好线程锁和条件变量的使用方法,对于编写高质量的并发程序有着重要的意义。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解C++11中的线程锁和条件变量 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • C程序 复利

    C程序 复利 使用攻略 介绍 C程序 复利 是一款基于C编写的计算复利的小工具。可以根据输入的本金、利率和时间计算出复利的本金、利息和总额。使用该工具可以方便快捷地计算不同本金、不同利率、不同时间下复利的本息和总额。 安装 下载C程序 复利 的源代码。 确认本地已经安装了C编译工具,如gcc、clang等。 打开终端,切换到C程序 复利 的源代码所在目录下。…

    C 2023年5月9日
    00
  • C/C++实现线性顺序表的示例代码

    下面是关于“C/C++实现线性顺序表”的完整攻略: 什么是线性顺序表 在计算机科学中,线性顺序表(Linear Sequences List)是一种连续的数据结构,也被称为数组,它由一组元素组成,并按线性顺序排列。线性顺序表中,每个元素和其相邻元素之间仅有了顺序关系,它们之间没有其他关系。通常情况下,线性顺序表采用数组来实现,支持随机访问操作。 C/C++实…

    C 2023年5月24日
    00
  • C语言文件操作详解以及详细步骤

    C语言文件操作详解以及详细步骤 在C语言中,文件操作是非常常见且必要的,本文将为您详细介绍C语言文件操作的相关知识。 打开文件 要打开一个文件,必须首先创建一个指向该文件的文件指针,并使用C语言标准库函数fopen()来打开该文件。fopen()函数的原型如下: FILE *fopen(const char *filename, const char *mo…

    C 2023年5月23日
    00
  • 一篇文章带你入门C++的异常处理

    一篇文章带你入门C++的异常处理 异常处理介绍 C++中有很多异常,比如说:除0异常、数组越界异常等。程序在执行中如果遇到异常,如果没有处理,将会导致程序崩溃。为了应对这种情况,我们可以使用C++的异常处理机制。 C++的异常处理机制的基本结构如下: try { // 可能会产生异常的代码 } catch(Exception e) { // 异常处理 } t…

    C 2023年5月22日
    00
  • 蓝屏代码0xc0000001是什么原因?蓝屏代码0xc0000001解决方法汇总

    蓝屏代码0xc0000001是什么原因? 在 Windows 操作系统中,蓝屏代码 0xc0000001 表示一个致命的系统错误,导致计算机无法继续工作。该错误代码通常与系统启动、恢复和内核数据读取有关。以下是可能导致蓝屏代码 0xc0000001 的几个常见原因: 损坏的引导记录或分区表; 损坏的操作系统文件; 损坏的驱动程序; 损坏的硬件,如硬盘、内存和…

    C 2023年5月24日
    00
  • C++类的返回值是*this的成员函数问题

    C++类的成员函数,除了默认拥有一个指向调用该函数的类对象的指针this外,还可以返回一个指向该类对象的引用。而对于返回值为该类对象本身的情况,实际上返回的是指向该类对象的引用*this。 以下是具体的实现过程及示例说明: 1. 类的定义 首先,假设我们定义了一个名为MyClass的类,其中包含两个私有成员变量x和y。 class MyClass { pri…

    C 2023年5月22日
    00
  • 逍遥自在学C语言 | 赋值运算符

    前言 在C语言中,赋值运算符用于将一个值赋给变量 这个过程分为两个步骤: 计算赋值运算符右侧的表达式 将结果赋给左侧的变量。 C语言提供了多个不同的赋值运算符,包括基本的赋值运算符、复合赋值运算符以及条件赋值运算符等 一、人物简介 第一位闪亮登场,有请今后会一直教我们C语言的老师 —— 自在。 第二位上场的是和我们一起学习的小白程序猿 —— 逍遥。 二、基本…

    C 2023年4月25日
    00
  • CMake的简单应用

    请允许我来讲解“CMake的简单应用”的完整攻略。 什么是 CMake CMake 是一个跨平台的编译构建工具,它可以用来自动生成 Makefile、Visual Studio 的项目、XCode 的工程等等编译构建相关的文件。 它可以帮助我们更方便地管理和构建跨平台的项目,提高开发效率和代码可维护性。下面我们将介绍如何使用 CMake 来构建项目。 CMa…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部