[编程基础] C++多线程入门5-使用互斥锁解决资源竞争
原始C++标准仅支持单线程编程。新的C++标准(称为C++11或C++0x)于2011年发布。在C++11中,引入了新的线程库。因此运行本文程序需要C++至少符合C++11标准。
5 使用互斥锁解决资源竞争
在本文中,我们将讨论如何使用互斥锁来保护多线程环境中的共享数据并避免资源竞争。为了解决多线程环境中的资源竞争,我们需要互斥锁,即每个线程都需要在修改或读取共享数据之前锁定互斥锁,并且在修改数据之后,每个线程都应解锁互斥锁。
5.1 std::mutex
在C++11线程库中,互斥锁位于mutex头文件中。表示互斥锁的类是std::mutex类
互斥锁有两种重要的方法:
- lock()
- unlock()
我们已经在上一篇文章中使用多线程钱包解释了资源竞争。在本文中,我们将看到如何使用std::mutex修复该多线程钱包中的资源竞争。由于电子钱包提供了在电子钱包中添加资金的服务,并且在不同线程之间使用了相同的电子钱包对象,因此我们需要在电子钱包的addMoney()方法中添加锁定,即在增加电子钱包的货币之前获取锁并在离开该钱包之前释放锁功能。让我们看一下代码:
内部维护货币并提供服务/功能的钱包类,即addMoney()。
该成员函数首先获取一个锁,然后将钱包对象的内部货币增加指定的数量,然后释放该锁。
#include<iostream>
#include<thread>
#include<vector>
#include<mutex>
class Wallet
{
int mMoney;
std::mutex mutex;
public:
Wallet() :mMoney(0) {}
int getMoney() { return mMoney; }
void addMoney(int money)
{
mutex.lock();
for (int i = 0; i < money; ++i)
{
mMoney++;
}
mutex.unlock();
}
};
现在,让我们创建5个线程,所有这些线程将共享Wallet类的同一对象,并使用其addMoney()成员函数并行向内部货币添加100000。因此,如果最初在钱包中的钱为0。那么在完成所有线程的执行后,在Wallet中的钱应该为500000。并且此互斥锁可确保电子钱包中的资金最终为500000。让我们测试一下:
#include<iostream>
#include<thread>
#include<vector>
#include<mutex>
class Wallet
{
int mMoney;
std::mutex mutex;
public:
Wallet() :mMoney(0) {}
int getMoney() { return mMoney; }
void addMoney(int money)
{
mutex.lock();
for (int i = 0; i < money; ++i)
{
mMoney++;
}
mutex.unlock();
}
};
int testMultithreadedWallet()
{
Wallet walletObject;
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i) {
threads.push_back(std::thread(&Wallet::addMoney, &walletObject, 100000));
}
for (int i = 0; i < threads.size(); i++)
{
threads.at(i).join();
}
return walletObject.getMoney();
}
int main()
{
int val = 0;
for (int k = 0; k < 10; k++)
{
if ((val = testMultithreadedWallet()) != 500000)
{
std::cout << "Error at count = " << k << " Money in Wallet = " << val << std::endl;
//break;
}
else
{
std::cout << "Now count = " << k << " Money in Wallet = " << val << std::endl;
//break;
}
}
return 0;
}
输出为:
Now count = 0 Money in Wallet = 500000
Now count = 1 Money in Wallet = 500000
Now count = 2 Money in Wallet = 500000
Now count = 3 Money in Wallet = 500000
Now count = 4 Money in Wallet = 500000
Now count = 5 Money in Wallet = 500000
Now count = 6 Money in Wallet = 500000
Now count = 7 Money in Wallet = 500000
Now count = 8 Money in Wallet = 500000
Now count = 9 Money in Wallet = 500000
可以保证不会发现钱包中的钱少于500000的单个情况。因为addMoney中的互斥锁可确保一旦一个线程完成了钱的修改,则只有其他任何线程才能修改Wallet中的钱。
但是,如果我们忘记在功能结束时解锁互斥锁,该怎么办?在这种情况下,一个线程将退出而不释放锁,而其他线程将保持等待状态。如果锁定互斥锁后发生某些异常,则可能发生这种情况。为了避免这种情况,我们应该使用std::lock_guard。
5.2 std::lock_guard
Lock_Guard是一个类模板,它实现了互斥锁的RAII。它将互斥体包装在其对象中,并将附加的互斥体锁定在其构造函数中。当调用它的析构函数时,它会释放互斥锁。让我们看看代码:
#include<iostream>
#include<thread>
#include<vector>
#include<mutex>
class Wallet
{
int mMoney;
std::mutex mutex;
public:
Wallet() :mMoney(0) {}
int getMoney() { return mMoney; }
void addMoney(int money)
{
// 在构造函数中,它锁定互斥锁 In constructor it locks the mutex
std::lock_guard<std::mutex> lockGuard(mutex);
for (int i = 0; i < money; ++i)
{
// If some exception occurs at this poin then destructor of lockGuard will be called due to stack unwinding.
// 如果在此位置发生异常,则由于堆栈展开,将调用lockGuard的析构函数。
mMoney++;
}
// Once function exits, then destructor of lockGuard Object will be called. In destructor it unlocks the mutex.
//一旦函数退出,则析构函数,将调用析构函数中的lockGuard对象,它解锁互斥锁。
}
};
int testMultithreadedWallet()
{
Wallet walletObject;
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i) {
threads.push_back(std::thread(&Wallet::addMoney, &walletObject, 100000));
}
for (int i = 0; i < threads.size(); i++)
{
threads.at(i).join();
}
return walletObject.getMoney();
}
int main()
{
int val = 0;
for (int k = 0; k < 10; k++)
{
if ((val = testMultithreadedWallet()) != 500000)
{
std::cout << "Error at count = " << k << " Money in Wallet = " << val << std::endl;
//break;
}
else
{
std::cout << "Now count = " << k << " Money in Wallet = " << val << std::endl;
//break;
}
}
return 0;
}
输出为:
Now count = 0 Money in Wallet = 500000
Now count = 1 Money in Wallet = 500000
Now count = 2 Money in Wallet = 500000
Now count = 3 Money in Wallet = 500000
Now count = 4 Money in Wallet = 500000
Now count = 5 Money in Wallet = 500000
Now count = 6 Money in Wallet = 500000
Now count = 7 Money in Wallet = 500000
Now count = 8 Money in Wallet = 500000
Now count = 9 Money in Wallet = 500000
5.3 参考
https://thispointer.com//c11-multithreading-part-5-using-mutex-to-fix-race-conditions/
[编程基础] C++多线程入门5-使用互斥锁解决资源竞争的更多相关文章
- 多任务-python实现-同步概念,互斥锁解决资源竞争(2.1.4)
@ 目录 1.同步的概念 2.解决线程同时修改全局变量的方式 3.互斥锁 1.同步的概念 同步就是协同步调,按照预定的先后次序进行运行,如你说完我在说 同步在子面上容易理解为一起工作 其实不是,同指的 ...
- [编程基础] C++多线程入门7-条件变量介绍
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 7 条件变 ...
- [编程基础] C++多线程入门6-事件处理的需求
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 6 事件处 ...
- [编程基础] C++多线程入门8-从线程返回值
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 8 从线程返回值 8 ...
- [编程基础] C++多线程入门4-数据共享和资源竞争
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++ 11标准. 4 数据共享和资源 ...
- [编程基础] C++多线程入门1-创建线程的三种不同方式
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 1 创建线程的三种不 ...
- [编程基础] C++多线程入门10-packaged_task示例
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 10 pa ...
- [编程基础] C++多线程入门9-async教程和示例
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 9 asy ...
- [编程基础] C++多线程入门3-小心地将参数传递给线程
原始C++标准仅支持单线程编程.新的C++标准(称为c++11或c++0x)于2011年发布.在c++11中,引入了新的线程库.因此运行本文程序需要C++至少符合c++11标准. 文章目录 3 小心地 ...
随机推荐
- python django超链接
之前用django框架打了一个简易的博客网站,现在说说怎么用django做超链接. 本文基于之前讲解的博客应用,如果只想学超链接请自行删减代码或评论提问. 首先,在templates文件夹下添加det ...
- python django搭建一个简易博客的解析(按照文件顺序逐一讲解)
上次讲解了一下各py文件的内容,但比较乱,所以这次整理了一个顺序版. 源代码请在http://github/Cheng0829/mysite自行下载 mysite: db.sqlite3:数据库文件. ...
- 9.channels layers
settings.py配置 # 存储在内存里 CHANNEL_LAYERS = { "default": { "BACKEND": "channels ...
- Pipeline流水线设计的最佳实践
谈到到DevOps,持续交付流水线是绕不开的一个话题,相对于其他实践,通过流水线来实现快速高质量的交付价值是相对能快速见效的,特别对于开发测试人员,能够获得实实在在的收益.很多文章介绍流水线,不管是j ...
- webgl(three.js)3D光伏,3D太阳能能源,3D智慧光伏、光伏发电、清洁能源三维可视化解决方案——第十六课
序: 能源是文明和发展的重要保障,人类命运不可避开的话题,无论是战争还是发展,都有它存在的身影.从石器时代到现代文明,人类的能源应用在进步,也在面临能源枯竭的危机与恐惧,而开发与应用可再生能源才是解决 ...
- 学习Rust第一天 Rust语言特点
学习Rust之前,我觉得应该首先了解Rust语言的设计目的是什么?为什么会诞生这门语言?这门语言和其他的语言有什么不同. Rust语言的设计特点 高性能:rust拥有和C++相近的性能表现,所以在嵌入 ...
- Arch Linux + KDE 配置&美化(持续更新~)
Arch Linux + KDE 配置&美化(持续更新~) 这篇文章着重记录archlinux + KDE的一个基本的配置过程.不包括安装过程(使用archInstall.sh).内容大概有以 ...
- 第2-3-3章 文件处理策略-文件存储服务系统-nginx/fastDFS/minio/阿里云oss/七牛云oss
目录 5.2 文件处理策略 5.2.1 FileStrategy 5.2.2 AbstractFileStrategy 5.2.3 LocalServiceImpl 5.2.4 FastDfsServ ...
- Go语言书籍推荐
附录: 其他书籍的介绍:https://github.com/golang/go/wiki/Books Go程序设计语言 (圣经) 作者 艾伦 A.A.多诺万 Kernighan 下载地址 中文版PD ...
- java 分布式游戏服务器框架,集群游戏服务器框架,游戏服务器网关框架 ioGame 网络游戏服务器框架
ioGame 国内首个基于蚂蚁金服 SOFABolt 的 java 网络游戏服务器框架:无锁异步化.事件驱动的架构设计 通过 ioGame 可以很容易的搭建出一个集群无中心节点.有状态多进程的分步式游 ...