多线程第七篇:互斥和同步总结

2024-08-24 16:18

本文主要是介绍多线程第七篇:互斥和同步总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

   同步:多个线程(进程)之间有严格的先后顺序,一个线程(进程)的执行,依赖于另一个.
   互斥:多个线程(进程)没有先后顺序,谁抢到算谁的.
 

注意:线程的创建和执行是分开的,并不是创建后立即执行,而是创建后,等待cpu调度.

window中的四种线程(进程)间同步与互斥.
criticalSection,mutex,event,semaphore

criticalSection:
     1.中文名称临界区或者关键段,用于线程间互斥.
     2.进入和离开必须成对出现.
     3.不是内核对象,不能用于进程间互斥,只能用于线程间互斥
     4.由于不是内核级对象,所以速度较快,效率较高.
使用:
     1.InitializeCriticalSection
     2.EnterCriticalSection
     3.LeaveCriticalSection
注意事项:
     临界区为子线程所有,主线程中使用临界区并不能实现互斥.


mutex:
     1.互斥区,用于线程(进程)间互斥
     2.记录拥有着,可以处理遗弃问题,当前使用该互斥区的线程(进程)占有该互斥区,并且,其他线程(进程)不能使用.
     3.可以在线程A中waitforsignalobject,在线程B中release.
     4.是内核对象,可以进行进程间互斥.
使用:
     1.CreateMutex或者OpenMutex
     2.ReleaseMutex 
     3.WaitForSingleObject
     4.CloseHandle
event:
     1.事件,用于线程(进程)间同步和互斥.
     2.是内核对象,不具有拥有权,即可以由一个线程(进程)触发,而由另一个线程(进程)恢复.
使用:
     1.CreateEvent
     2.SetEvent
     3.ResetEvent
     4.PulseEvent
     5.WaitForSingleObject
     6.CloseHandle

semaphore:
     1.信号量,用于线程(进程)间同步和互斥.
     2.是内核对象,不具有拥有权,可以在不同的线程(进程)中进行PV操作.
使用:
     1.CreateSemaphore 或OpenSemaphore
     2.ReleaseSemaphore(+n)
     3.WaitForSingleObject(-1)
     4.CloseHandle

遗弃问题:
     由于临界区是线程级别使用的,线程的操作都是由主线程控制的,当一个正在使用临界区的线程突然崩溃会怎么样呢?由于临界区会记录使用的线程,所以同样可以处理遗弃问题.

遗弃问题:
     由于进程(线程)突然死亡,而没有进行任何善后处理,这样会造成资源永远被占用,需要一种方法来解决.
 
解决之道
     当mutex使用者突然死了,那么mutex归零,等待其他使用者占有然后使用.
为什么event和semaphore不可以:
     因为event和semaphore并不记录其使用者,他的使用者死了event和semaphore并不知道,他只知道自己被使用,等待被释放.
     如果event和semaphore的使用者突然死亡,而其进程都在等待event和semaphore可用,那么这就造成了死锁.


为什么mutex和临界区(critical_section)不能进行同步:
   因为mutex和critical不能再主线程中使用,而信号量和事件都可以在主线程中使用.
   回忆一下,我们每次进行线程间同步的时候,总是先在主线程先设置事件或者信号量的状态,然后再在线程中设置,这样线程就可以使得线程有先后顺序了,即实现同步.核心在于主线程总是第一个执行,然后再执行子线程,这样就可以有先后顺序,如果不在主线程中设置,只是让子线程进行同步,也可以这需要一个全局变量,来进行检测是不是该这个线程执行.

我们用临界区+全局变量来实现一个线程同步:
#include <iostream>
#include <windows.h>
#include <process.h>
int g_count = 0;
CRITICAL_SECTION all;
unsigned int __stdcall producer (void *)
{
int ID = 0;
bool flage = true ;
while ( flage   ){
EnterCriticalSection (&all );
if ( g_count == ID ){
std ::cout << "producer"<< std ::endl ;
flage = false ;
g_count ++;
}
LeaveCriticalSection (&all );
}
return 0;
}
unsigned int __stdcall consumer (void *)
{
int ID = 1;
bool flage = true ;
while ( flage   ){
EnterCriticalSection (&all );
if ( g_count == ID ){
std ::cout << "consumer"<< std ::endl ;
flage = false ;
}
LeaveCriticalSection (&all );
}
return 0;
}
int main ()
{
InitializeCriticalSection(& all );
HANDLE hproducer = (HANDLE ) _beginthreadex( NULL ,0,producer , NULL,0, NULL );
HANDLE hconsumer = (HANDLE ) _beginthreadex( NULL ,0,consumer , NULL,0, NULL );
WaitForSingleObject( hproducer ,INFINITE );
WaitForSingleObject( hconsumer ,INFINITE );
return 0;
}

扩展:
      可不可以让信号量和事件实现遗弃呢? 
      当然可以,我们可以在线程调用信号量或事件的时候,在内核对象中添加该进程号.在进程表或其他地方注册一个回调函数,此函数功能是当该进程或线程崩溃的时候,通知相关内核对象(信号量内核对象或者事件内核内核对象),此线程已崩溃,删除该线程相关信息.
     此机制类似于epoll机制,有兴趣可以了解一下.


这篇关于多线程第七篇:互斥和同步总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1102972

相关文章

Python 中的异步与同步深度解析(实践记录)

《Python中的异步与同步深度解析(实践记录)》在Python编程世界里,异步和同步的概念是理解程序执行流程和性能优化的关键,这篇文章将带你深入了解它们的差异,以及阻塞和非阻塞的特性,同时通过实际... 目录python中的异步与同步:深度解析与实践异步与同步的定义异步同步阻塞与非阻塞的概念阻塞非阻塞同步

java常见报错及解决方案总结

《java常见报错及解决方案总结》:本文主要介绍Java编程中常见错误类型及示例,包括语法错误、空指针异常、数组下标越界、类型转换异常、文件未找到异常、除以零异常、非法线程操作异常、方法未定义异常... 目录1. 语法错误 (Syntax Errors)示例 1:解决方案:2. 空指针异常 (NullPoi

Java使用多线程处理未知任务数的方案介绍

《Java使用多线程处理未知任务数的方案介绍》这篇文章主要为大家详细介绍了Java如何使用多线程实现处理未知任务数,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 知道任务个数,你可以定义好线程数规则,生成线程数去跑代码说明:1.虚拟线程池:使用 Executors.newVir

Java反转字符串的五种方法总结

《Java反转字符串的五种方法总结》:本文主要介绍五种在Java中反转字符串的方法,包括使用StringBuilder的reverse()方法、字符数组、自定义StringBuilder方法、直接... 目录前言方法一:使用StringBuilder的reverse()方法方法二:使用字符数组方法三:使用自

JAVA封装多线程实现的方式及原理

《JAVA封装多线程实现的方式及原理》:本文主要介绍Java中封装多线程的原理和常见方式,通过封装可以简化多线程的使用,提高安全性,并增强代码的可维护性和可扩展性,需要的朋友可以参考下... 目录前言一、封装的目标二、常见的封装方式及原理总结前言在 Java 中,封装多线程的原理主要围绕着将多线程相关的操

Linux搭建Mysql主从同步的教程

《Linux搭建Mysql主从同步的教程》:本文主要介绍Linux搭建Mysql主从同步的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux搭建mysql主从同步1.启动mysql服务2.修改Mysql主库配置文件/etc/my.cnf3.重启主库my

Python依赖库的几种离线安装方法总结

《Python依赖库的几种离线安装方法总结》:本文主要介绍如何在Python中使用pip工具进行依赖库的安装和管理,包括如何导出和导入依赖包列表、如何下载和安装单个或多个库包及其依赖,以及如何指定... 目录前言一、如何copy一个python环境二、如何下载一个包及其依赖并安装三、如何导出requirem

Rust格式化输出方式总结

《Rust格式化输出方式总结》Rust提供了强大的格式化输出功能,通过std::fmt模块和相关的宏来实现,主要的输出宏包括println!和format!,它们支持多种格式化占位符,如{}、{:?}... 目录Rust格式化输出方式基本的格式化输出格式化占位符Format 特性总结Rust格式化输出方式

Python中多线程和多进程的基本用法详解

《Python中多线程和多进程的基本用法详解》这篇文章介绍了Python中多线程和多进程的相关知识,包括并发编程的优势,多线程和多进程的概念、适用场景、示例代码,线程池和进程池的使用,以及如何选择合适... 目录引言一、并发编程的主要优势二、python的多线程(Threading)1. 什么是多线程?2.

Java中将异步调用转为同步的五种实现方法

《Java中将异步调用转为同步的五种实现方法》本文介绍了将异步调用转为同步阻塞模式的五种方法:wait/notify、ReentrantLock+Condition、Future、CountDownL... 目录异步与同步的核心区别方法一:使用wait/notify + synchronized代码示例关键