C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁

这篇具有很好参考价值的文章主要介绍了C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、多线程的状态

  • 初始化 (Init):该线程正在被创建。
  • 就绪 (Ready):该线程在就绪列表中,等待CPU的调度。
  • 运行 (Running):该线程正在运行。
  • 阻塞(Blocked):该线程被阻塞挂起。Blocked状态包括:pend(锁、事件、信号量等阻塞)、suspend(主动pend)、delay(延时阻塞)、pendtime(因为锁、事件、信号量时间等超时等待)。
  • 退出(Exit):该线程运行结束,等待父线程后收其控制块资源。

C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁,多线程学习,c++,学习,开发语言

二、多线程之间的竞争关系和临界区

1、竞争关系(Race condition)

多个线程同时读写共享资源
例如:

#include<iostream>
#include<thread>
#include<string>
using namespace std;
void Test()
{
	cout << "---------------" << endl;
	cout << "Test 1 " << endl;
	cout << "Test 2 " << endl;
	cout << "-----------------" << endl;
}

int main()
{
	for (int i = 0; i < 10; i++)
	{
		thread th(Test);
		th.detach();
	}
	getchar();
	return 0;
}

C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁,多线程学习,c++,学习,开发语言

我们创建了10个线程,但是每个线程在运行时都是无规律的,对于相同的输出都是处于竞争状态。

2、临界区(Critical Section)

我们使用C++ 11 自带的mutex(互斥锁)来解决竞争状态。
C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁,多线程学习,c++,学习,开发语言
原理:
当n个线程要访问我们临界区时,只允许一个线程进入,其余线程会阻塞在临界区外,每当一个线程运行完临界区代码后,会允许阻塞在临界区外的一个线程再次访问临界区。

3、lock()和try_lock()区别

  • lock():**lock 函数用于获取互斥锁。**如果当前互斥锁已被其他线程占用,则 lock 函数将阻塞当前线程,直到互斥锁可用为止。一旦成功获取到互斥锁,该线程将独占互斥锁,并可以继续执行后续的操作。在完成互斥锁所需的操作后,线程应该调用 unlock 函数来释放互斥锁,以便其他线程可以获取它。阻塞式的,会一直等待直到成功获取到互斥锁。

  • try_lock()try_lock 函数用于尝试获取互斥锁,但是它并不会阻塞当前线程。如果当前互斥锁可用,try_lock 函数将立即获取到互斥锁,并返回 true。如果当前互斥锁被其他线程占用,try_lock 函数将立即返回 false,而不会阻塞线程。通过检查返回值,线程可以根据情况选择等待或放弃获取互斥锁。非阻塞式的,立即返回获取互斥锁的结果,线程可以根据返回值做出相应的处理。
    例如:这里有10个线程,只有两个线程进入,而其它8个线程并不会阻塞住。
    C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁,多线程学习,c++,学习,开发语言

  • 使用 lock 函数时要注意避免死锁的情况,而 try_lock 函数则可以更灵活地处理互斥锁的获取失败情况。

4、互斥锁的坑(线程抢占)

C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁,多线程学习,c++,学习,开发语言
可以看到在这段代码中,本来1号线程运行完,执行unlock()后,应该其他阻塞的线程应该进入代码内,但是发现进入的依旧是1号ID的线程。
原因:当我们执行完unlock()后,因为代码执行速度就几微秒的时间,但是我们CPU调度探测一次得过一段时间,所以导致了这个问题。(也就是说我们执行的速度快于检测的速度,导致我们线程1号执行完后又再次进入循环,又把资源锁上了)
解决:只需要在unlock()后面等待一段时间就可以,让我们CPU有充裕的时间来调度检测。C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁,多线程学习,c++,学习,开发语言

三、互斥锁(mutex)

上面样例中设计到这里就不在过多描述

四、超时锁(timed_mutex)

C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁,多线程学习,c++,学习,开发语言
C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁,多线程学习,c++,学习,开发语言

超时锁可以让阻塞的线程不断尝试进入,如果超过等待时间,就会执行日志结果,可以帮助我们更好的检查线程状态,检查是否超时或者是否存在死锁。

五、可重入锁(recursive_mutex)

1.可重入锁可以解决哪类情况?

如果直接用互斥锁C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁,多线程学习,c++,学习,开发语言
显然会直接报错,因为我们要进入下一个锁内需要先解除当前锁C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁,多线程学习,c++,学习,开发语言

显然是让人头疼麻烦的,我们必须每次得考虑解锁的时机。为了解决此类情况,我们有了可重入锁。

2.可重入锁。

C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁,多线程学习,c++,学习,开发语言
功能如可以多重进入锁区域,要注意的是,进入几个锁就要在最后解除几个锁,这样才能让其他阻塞线程进入。

六、共享锁(shared_mutex)

C++ 14中引入了shared_timed_mutex;
C++ 17中引入了shared_mutex;

1、 共享锁的功能

假设有6个线程同时要访问同一片区域数据,有5个线程要读数据,而有一个线程要写数据,那么共享锁可以让读线程的5个线程同时进行读,然后阻塞写数据的线程。当写线程进入时,阻塞5个读的线程,写线程开始写数据。这就是共享锁的基本作用。

2、代码

#include<iostream>
#include<thread>
#include<shared_mutex>
using namespace std;
shared_mutex smux;
int num;
void thread_read(int i)
{
	for (;;)
	{
	smux.lock_shared();
	cout << "num is :" <<num<< endl;
	this_thread::sleep_for(1000ms);
	smux.unlock_shared();
	this_thread::sleep_for(10ms);
	}
}
void thread_write(int i)
{
	for (;;)
	{
	smux.lock_shared();
	num++;
	cout << "add num" << endl;
	this_thread::sleep_for(1000ms);
	smux.unlock_shared();
	this_thread::sleep_for(10ms);
	}

}

int main()
{
	for (int i = 0; i < 5; i++)
	{
		thread th(thread_read,i + 1);
		this_thread::sleep_for(50ms);
		th.detach();
	}
	for (int i = 0; i < 2; i++)
	{
		thread th(thread_write, i + 1);
		this_thread::sleep_for(50ms);
		th.detach();
	}
	getchar();
	return 0;
}

C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁,多线程学习,c++,学习,开发语言文章来源地址https://www.toymoban.com/news/detail-795311.html

到了这里,关于C++多线程学习[四]:多线程的通信和同步、互斥锁、超时锁、共享锁的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【C++】Windows下共享内存加信号量实现进程间同步通信

    目录 一,函数清单 1.CreateFileMapping 方法 2.OpenFileMapping 方法 3.MapViewOfFile 方法 4.UnmapViewOfFile 方法 5.CreateSemaphore 方法 6. OpenSemaphore 方法 7.WaitForSingleObject 方法 8.ReleaseSemaphore 方法 9.CloseHandle 方法 10.GetLastError 方法 二,单共享内存单信号量-进程间单向通信 共享内存管理文

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

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

    2024年02月05日
    浏览(36)
  • 线程同步与互斥

    目录 前言:基于多线程不安全并行抢票 一、线程互斥锁 mutex 1.1 加锁解锁处理多线程并发  1.2 如何看待锁 1.3 如何理解加锁解锁的本质 1.4 C++RAII方格设计封装锁 前言:基于线程安全的不合理竞争资源 二、线程同步 1.1 线程同步处理抢票 1.2 如何理解\\\"条件变量\\\" 1.3 如何理解条

    2024年02月10日
    浏览(25)
  • 【Linux】线程同步和互斥

    1.临界资源:多线程执行流共享的资源,且一次只能允许一个执行流访问的资源就叫做临界资源。(多线程、多进程打印数据) 2.临界区:每个线程内部,访问临界资源的代码,就叫做临界区。 3.互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对

    2024年02月08日
    浏览(31)
  • Linux-线程的同步与互斥

    🚀 临界资源:多线程指行流共享的资源叫做临界资源。 🚀 临界区:每个线程内部访问临界资源的代码片段叫做临界区。 🚀 互斥:任何时刻,互斥保证只有一个指行流进入临界区,访问临界资源,通常是对临界区起保护作用。 🚀 原子性:不被任何调度所打断的操作,该

    2024年02月09日
    浏览(34)
  • Linux——多线程,互斥与同步

    目录 一.linux互斥 1.进程线程间的互斥相关背景概念 2.互斥量mutex 3.加锁互斥锁mutex 4.锁的底层原理  二.可重入VS线程安全 1.概念 2.常见的线程不安全的情况 3.常见的线程安全的情况  4.常见不可重入的情况  5..常见可重入的情况 6.可重入与线程安全联系  三.死锁 1.死锁四个必

    2024年02月05日
    浏览(24)
  • 【Linux】多线程互斥与同步

    互斥 指的是一种机制,用于确保在同一时刻只有一个进程或线程能够访问共享资源或执行临界区代码。 互斥的目的是 防止多个并发执行的进程或线程访问共享资源时产生竞争条件,从而保证数据的一致性和正确性 ,下面我们来使用多线程来模拟实现一个抢票的场景,看看所

    2024年02月09日
    浏览(28)
  • Linux——线程的同步与互斥

    目录 模拟抢火车票的过程 代码示例 thread.cc Thread.hpp 运行结果 分析原因 tickets减到-2的本质  解决抢票出错的方案 临界资源的概念 原子性的概念 加锁 定义 初始化 销毁 代码形式如下 代码示例1: 代码示例2: 总结 如何看待锁 申请失败将会阻塞  pthread_mutex_tyrlock 互斥锁实现

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

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

    2024年02月10日
    浏览(34)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包