线程间互斥-mutex互斥锁和lock_guard

这篇具有很好参考价值的文章主要介绍了线程间互斥-mutex互斥锁和lock_guard。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

要点

  • 锁+双重判断的技法

  • 竟态条件:多线程程序执行的结果一致,不会随着CPU对线程不同的调用顺序

线程间安全实例——3个窗口同时卖票

线程不安全的代码如下

int ticketCount = 100; // 100张车票
// 模拟10个窗口同时卖票
void sellTicket(int index)
{
	while (ticketCount > 0)
	{
		//cout << "窗口:" << index << "卖出第:" << ticketCount << "张票" << endl;
		cout << ticketCount << endl; // 打印当前剩余票数
		ticketCount--;
		std::this_thread::sleep_for(std::chrono::milliseconds(10));
	}
}
int main()
{
	list<std::thread> tlist;
	for (int i = 1; i <= 3; ++i)
	{
		tlist.push_back(std::thread(sellTicket, i));
	}

	for (std::thread& t : tlist)
	{
		t.join();
	}

	cout << "所有窗口卖票结束!" << endl;
	return	0;
}

输出的部分结果里有很多重复的数字,相当于同一张票被卖出多次,原因在于

ticketCount–; 是线程不安全的,理由如下

线程间互斥-mutex互斥锁和lock_guard

某一时刻ticketCount = 99,thread1此时调用ticketCount–,执行到sub eax时执行线程切换到另一个线程thread2中,此时ticketCount仍为99,执行完3条汇编后ticketCount = 98,此时再切换回thread1,继续执行完后面汇编,也使ticketCount = 98,这里就导致同时输出两次98;

解决方法

保证某一线程ticketCount–没做完,其他线程不允许做ticketCount–操作,可以使用加mutex互斥锁的方法:

void sellTicket(int index)
{
	mtx.lock();
	while (ticketCount > 0)
	{
		//cout << "窗口:" << index << "卖出第:" << ticketCount << "张票" << endl;
		cout << ticketCount << endl; // 打印当前剩余票数
		ticketCount--;
		std::this_thread::sleep_for(std::chrono::milliseconds(10));
	}
	mtx.unlock();
}

但是这样的加锁问题在于锁粒度太大,可以进一步缩小,采用锁+双重判断的方法:

void sellTicket(int index)
{
	while (ticketCount > 0)
	{
		mtx.lock();
		if (ticketCount > 0) // !!!
		{
			cout << "窗口:" << index << "卖出第:" << ticketCount << "张票" << endl;
			//cout << ticketCount << endl; // 打印当前剩余票数
			ticketCount--;
		}
		mtx.unlock();
		std::this_thread::sleep_for(std::chrono::milliseconds(10));
	}
}

这里if (ticketCount > 0) 判断必须加,如果不加,当ticketCount = 1时,切换到其他线程卖完后ticketCount = 0 ,切换回原线程继续卖票就变成可以卖第0张票,不合法

lock_guard和unique_lock

lock_guard

lock_guard不能用在函数参数传递返回过程中,只能用在简单的临界区代码段互斥操作;

类似于scoped_str

lock_guard是对std::mutex的封装,拷贝构造和赋值函数被delete,它是RAII技术的实践,创建对象时就加锁,出作用域析构调用解锁,用lock_guard替换上面案例的mutex:

void sellTicket(int index)
{
	while (ticketCount > 0)
	{
		//mtx.lock();
		{
			lock_guard<std::mutex> lock(mtx); // 
			if (ticketCount > 0)
			{
				cout << "窗口:" << index << "卖出第:" << ticketCount << "张票" << endl;
				ticketCount--;
			}
		} 
		//mtx.unlock();
		std::this_thread::sleep_for(std::chrono::milliseconds(10));
	}
}

unique_lock

unique_lock不仅可用在函数参数传递或返回过程中,还能用在函数调用中,如 和条件变量函数一起使用:

unique_lock<std::mutex> lck(mtx);

cv.wait(lck); // => #1.使线程进入等待状态 #2.lck.unlock可以把mtx给释放掉

unique_lock 也是对mutex的封装,它也可以像lock_guard一样使用,同时它支持手动调用lock unlock,会帮助检查是否忘记调用unlock,使用例子如下:文章来源地址https://www.toymoban.com/news/detail-438224.html

void sellTicket(int index)
{
	while (ticketCount > 0)
	{
		{
			unique_lock<std::mutex> lck(mtx);
			lck.lock();
			if (ticketCount > 0)
			{
				cout << "窗口:" << index << "卖出第:" << ticketCount << "张票" << endl;
				ticketCount--;
			}
			lck.unlock();
		} 
		std::this_thread::sleep_for(std::chrono::milliseconds(10));
	}
}

到了这里,关于线程间互斥-mutex互斥锁和lock_guard的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++11互斥量mutex使用详解

    mutex又称互斥量,C++ 11中与 mutex相关的类(包括锁类型)和函数都声明在#include头文件中,所以如果你需要使用 std::mutex,就必须包含#include头文件。 C++11提供如下4种语义的互斥量(mutex) : std::mutex,独占的互斥量,不能递归使用。 std::time_mutex,带超时的独占互斥量,不能递

    2024年02月16日
    浏览(29)
  • 【Rust 基础篇】Rust 互斥器(Mutex)

    在 Rust 中,互斥器(Mutex)是一种用于在多个线程之间共享数据的并发原语。互斥器提供了一种安全的方式,允许多个线程访问共享数据,但每次只允许一个线程进行写操作。本篇博客将详细介绍 Rust 中互斥器的使用方法,包含代码示例和对定义的详细解释。 在 Rust 中,我们

    2024年02月15日
    浏览(73)
  • 互斥锁、自旋锁、读写锁和文件锁

    互斥锁(mutex)又叫互斥量,从本质上说是一把锁,在访问共享资源之前对互斥锁进行上锁,在访问完成后释放互斥锁(解锁);对互斥锁进行上锁之后,任何其它试图再次对互斥锁进行加锁的线程都会被阻塞,直到当前线程释放互斥锁。如果释放互斥锁时有一个以上的线程

    2024年02月12日
    浏览(25)
  • 【Linux】线程互斥 -- 互斥锁 | 死锁 | 线程安全

    我们写一个多线程同时访问一个全局变量的情况(抢票系统),看看会出什么bug: 假如创建4个线程同时抢票,总票数有10000张,每个线程抢到票以后减一,按照正常情况我们应该是抢票到0截至。 多个线程交叉执行本质:就是让调度器尽可能的频繁发生线程调度与切换 线程一般

    2024年02月14日
    浏览(29)
  • Java多线程:读写锁和两种同步方式的对比

    读写锁ReentrantReadWriteLock概述 大型网站中很重要的一块内容就是数据的读写,ReentrantLock虽然具有完全互斥排他的效果(即同一时间只有一个线程正在执行lock后面的任务),但是效率非常低。所以在JDK中提供了一种读写锁ReentrantReadWriteLock,使用它可以加快运行效率。 读写锁表

    2023年04月09日
    浏览(29)
  • 多线程(线程互斥)

    学习了前面有关线程库的操作后,我们就可以模拟抢票的过程 假设我们创建四个线程,分别代表我们的用户 然后设定总票数为1000张,四个线程分别将进行循环抢票操作,其实就是循环对票数进行打印,并进行对应的减减操作 一旦票数为0,也就是票没有了,我们就让线程从

    2024年02月07日
    浏览(23)
  • 【探索Linux】—— 强大的命令行工具 P.20(多线程 | 线程互斥 | 互斥锁 | 死锁 | 资源饥饿)

    在上一篇文章中,我们对多线程编程的基础知识进行了深入的探讨,包括了线程的概念、线程控制以及分离线程等关键点。通过这些内容的学习,我们已经能够理解并实现简单的多线程程序。然而,随着程序复杂度的提升,仅仅掌握这些基础是远远不够的。在多线程环境下,

    2024年02月05日
    浏览(30)
  • Linux——线程3|线程互斥和同步

    我们上一篇提到过,多个线程执行下面代码可能会出错,具体原因可查看上一篇Linux博客。 为避免这种错误的出现,我们可采用加锁保护。 PTHREAD_MUTEX_INITIALIZER 用pthread_mutex_t定义一把锁。ptherad_mutex_init是对锁进行初始化的函数。如果这把锁是全局的并且是静态定义的,我们可

    2024年02月05日
    浏览(36)
  • 【Linux】多线程2——线程互斥与同步/多线程应用

    💭上文主要介绍了多线程之间的独立资源,本文将详细介绍多线程之间的 共享资源 存在的问题和解决方法。 intro 多线程共享进程地址空间,包括创建的全局变量、堆、动态库等。下面是基于全局变量实现的一个多线程抢票的demo。 发现错误:线程抢到负数编号的票,为什么

    2024年02月10日
    浏览(35)
  • 多线程基础入门【Linux之旅】——上篇【线程控制,线程互斥,线程安全】

    目录 前文 回望页表 一,什么是线程 二,使用 pthread_create (线程创建) 三,线程控制 1 ,线程共享进程数据,但也拥有自己的一部分数据: 2, 线程  VS 进程优点 3,pthread_join(等待线程) 4,pthread_exit (线程终止) 5, pthread_cancel (线程取消) 6. pthread_t 类型 7.  pthread_detac

    2024年01月16日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包