首页 > 编程语言 >C++互斥锁

C++互斥锁

时间:2024-10-15 22:45:39浏览次数:5  
标签:std mtx lock 解锁 C++ 互斥 线程

互斥锁(Mutex,全称:Mutual Exclusion)是一种用于多线程编程中的同步机制,用来确保在同一时刻只有一个线程可以访问共享资源。它通过锁定机制防止多个线程同时对共享资源进行读写操作,从而避免数据竞争和不一致性问题。

互斥锁的核心思想是保证互斥访问,即当一个线程持有互斥锁并正在访问共享资源时,其他线程必须等待,直到该线程释放锁后,其他线程才能获得锁并访问资源。

互斥锁的特点

  1. 互斥性:一次只能有一个线程持有锁,其他线程必须等待。
  2. 锁定与解锁:线程通过调用锁定函数来请求锁定资源,完成对资源的访问后,再通过解锁函数释放锁。
  3. 阻塞机制:如果一个线程尝试锁定一个已经被其他线程锁住的资源,它会进入阻塞状态,直到锁被释放。
  4. 避免数据竞争:互斥锁可以避免多个线程同时访问共享资源引发的数据竞争问题,确保对资源的访问是安全的。

常见应用场景

  • 临界区保护:临界区是多线程中同时访问共享资源的代码段。互斥锁用于保护这些临界区,确保同一时刻只有一个线程可以进入。
  • 资源共享:当多个线程需要访问同一文件、共享内存或者全局变量时,互斥锁可以确保这些资源的访问是同步的。

互斥锁的使用

在C++中,互斥锁可以通过pthread_mutex_t(POSIX线程)或std::mutex(C++11)来实现。在C++11之前,POSIX线程库pthread中提供了对互斥锁的支持,而C++11标准引入了std::mutex,这是使用互斥锁更现代的方式。

C++11 中 std::mutex 的基本用法

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

std::mutex mtx;  // 创建一个互斥锁
int shared_resource = 0;

void thread_func() {
    // 加锁
    mtx.lock();
    // 保护共享资源的代码
    shared_resource++;
    std::cout << "Shared resource: " << shared_resource << std::endl;
    // 解锁
    mtx.unlock();
}

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

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

    return 0;
}

代码解释:

  • 互斥锁声明std::mutex mtx; 用于声明一个互斥锁对象mtx
  • 加锁(lock)mtx.lock(); 在访问共享资源前对互斥锁加锁,防止其他线程同时访问该资源。
  • 解锁(unlock)mtx.unlock(); 访问完成后解锁,允许其他线程继续访问该资源。
  • 线程同步:通过mtx.lock()mtx.unlock()确保同一时刻只有一个线程可以修改shared_resource变量,避免数据竞争。

使用 std::lock_guard 进行自动解锁

手动加锁和解锁可能会导致因忘记解锁或异常发生而导致死锁。为了解决这个问题,C++11 提供了std::lock_guard来自动管理锁的生命周期,当锁对象超出作用域时,自动解锁。

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

std::mutex mtx;
int shared_resource = 0;

void thread_func() {
    std::lock_guard<std::mutex> guard(mtx);  // 自动加锁并在函数结束时自动解锁
    shared_resource++;
    std::cout << "Shared resource: " << shared_resource << std::endl;
}

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

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

    return 0;
}

在这种方式下,不需要显式调用lock()unlock(),当lock_guard对象离开作用域时,它会自动调用unlock(),从而避免由于异常或复杂的控制流导致的未解锁问题。

互斥锁的常见问题

  1. 死锁:如果多个线程之间发生循环等待现象,线程相互持有对方需要的资源,并且都不释放,导致程序无法继续执行。这种情况称为死锁。

    • 解决办法:避免多个锁嵌套使用,确保加锁的顺序一致,或使用std::try_lock()来避免死锁。
  2. 优先级反转:高优先级的线程被低优先级的线程阻塞的现象。通常出现在多优先级的系统中,低优先级的线程持有锁,导致高优先级线程无法获得锁。

    • 解决办法:操作系统的调度器可以通过优先级继承协议来缓解这个问题。
  3. 性能问题:在高并发环境中,如果多个线程频繁争夺锁,可能会导致性能瓶颈。锁竞争的开销以及上下文切换的开销会影响系统的性能。

    • 解决办法:尽量减少锁的粒度,使用更细粒度的锁或无锁数据结构来优化性能。

总结

  • 互斥锁是一种用于多线程同步的机制,它通过保证同一时刻只有一个线程访问共享资源,避免了数据竞争。
  • C++11 提供了std::mutexstd::lock_guard等便捷工具来简化锁的使用并避免错误。
  • 互斥锁的滥用可能导致死锁、优先级反转等问题,因此在设计多线程程序时,需要谨慎处理同步机制。

标签:std,mtx,lock,解锁,C++,互斥,线程
From: https://www.cnblogs.com/chentiao/p/18468699

相关文章

  • c++实验1
    实验任务1:1#include<iostream>2#include<string>3#include<vector>4#include<algorithm>5usingnamespacestd;6template<typenameT>7voidoutput(constT&C);8voidtest1();9voidtest2();10voidtest......
  • C++中的不安全函数
    不安全函数(UnsafeFunctions)通常指那些在特定条件下可能导致程序错误、数据损坏或安全漏洞的函数。在编程中,不安全函数可能表现为以下几种情况:缓冲区溢出:当函数在处理数据时没有检查输入的大小,可能导致超出预分配内存空间的写入,造成数据破坏或程序崩溃。例如,在C和C++中,strcpy、......
  • 实验一 现代C++编程初体验
    任务1//现代C++标准库、算法库体验//本例用到以下内容://1.字符串string,动态数组容器类vector、迭代器//2.算法库:反转元素次序、旋转元素//3.函数模板、const引用作为形参#include<iostream>#include<string>#include<vector>#include<algorithm>using......
  • C++(nullptr、类型推导、初始化列表、)
    1.nullptr(掌握)nullptr是C++11推出的新的空指针,用于代替C语言的NULL。#include<iostream>usingnamespacestd;voidfunc(inti){cout<<"A"<<i<<endl;}voidfunc(char*c){cout<<"B"<<c<<en......
  • 实验1 现代C++编程初体验
    实验任务1:task1.cpp1//现代C++标准库、算法库体验2//本例用到以下内容:3//1.字符串string,动态数组容器类vector、迭代器4//2.算法库:反转元素次序、旋转元素5//3.函数模板、const引用作为形参67#include<iostream>8#include<string......
  • C++使用MySQL官方的C API访问MySQL数据库
    这篇文章是一个简单的C++使用MySQL官方的CAPI访问MySQL数据库的代码示例。//main.h#ifndef_H_#define_H_#include<stdio.h>#include<Windows.h>#include<mysql.h>#pragmacomment(lib,"libmysql.lib")#defineinsert_prepare"insertintotest_tbvalue......
  • c++不同容器之间的转换
    在C++中,不同容器之间的转换主要依赖于标准库的迭代器。大部分标准容器提供了兼容的构造函数或函数接口来从其他容器转换或初始化数据。下面是几种常见容器的转换方式:1.vector到set的转换#include<iostream>#include<vector>#include<set>intmain(){std::vec......
  • 实验1 现代C++编程初体验
    实验任务1代码#include<iostream>#include<vector>#include<string>#include<algorithm>#include<numeric>#include<iomanip>usingnamespacestd;template<typenameT>voidoutput(constT&c);intrand_int_1......
  • 实验1 现代C++编程初体验
    一、实验目的体验现代C++标准库、算法库用法灵活组合使用现代C++基础语言特性(数据表示、分支、循环、函数)和标准库,编程解决简单、基础问题编程代码过程中,注意编码素养。关注代码表达,提升代码的可读性、易于维护性二、实验准备系统浏览教材以下章节,对现代c++基础语言特性和......
  • C++的仿函数functor
    C++的仿函数functor详细内容仿函数(Functor)是C++中的一种设计模式,也叫函数对象。仿函数是一个重载了operator()的类或结构体,它可以像普通函数一样被调用。这使得它具有类似函数的行为,但实际上它是一个对象,因此可以拥有状态(成员变量)和更多的灵活性。仿函数的主要用途是:可以......