Net CLR 中的同步机制(一): 互斥体

.NET 多线程 互斥锁lock、互斥锁Monitor 、读写锁ReaderWriteLockSlim、spinLock、Semaphore 线程同步 注意:定义的锁对象应该是 私有的,静态的,只读的,引用类型的对象,这样可以防止外部改变锁对象。注意:定义的锁对象应该是 私有的,静态的,只读的,引用类型的对象,这样可以防止外部改变锁对象。注意:定义的锁对象应该是 私有的,静态的,只读的,引用类型的对象,这样可以防止外部改变锁对象。作用:将会锁住代码块的内容,并阻止其他线程进入该代码块,直到该代码块运行完成,释放该锁。作用:将会锁住代码块的内容,并阻止其他线程进入该代码块,直到该代码块运行完成,释放该锁。问题: 既然读读不互斥,为何还要加读锁。 阅读详情

来自:http://www.cnblogs.com/haoxinyue/archive/2013/01/29/2882152.html

随着软硬件技术的发展,无论是在Web服务或者云计算,还是单一的应用程序,串行方式编写的软件越来越少,我们总是可以看见并行的存在。但是并行并不是适合于每一种场景,也完全不是将工作扔到线程池中排队运行那么简单。

由于在进程中,多个线程可能需要访问相同的虚拟内存地址空间,如果不进行控制就很容易出现数据竞争的并发问题,大多是因为操作非原子性和线程时间片的原因引起的,导致的现象会是抛出异常,程序崩溃,数据的值和期望不一致,数据破坏等等,关键有的时候还会随机出现一些问题,这次运行正确,下一次就不正确了,这些问题都不是简单的单元测试就可以测试出来的。为了解决这些问题,windows就提供了同步机制,同步就是唯一能够保证让多线程能正确的使用共享的可变状态的技术。

同步一般分为两种:数据同步和控制同步。

数据同步:一般是指同步的访问某个共享资源主要是内存数据,多个程序以并行的方式使用相同的资源时不会产生干扰。常见的有:lock,Mutex,Monitor,Semaphore等等

控制同步:多线程的运行依赖于程序的控制流,一个线程的运行往往要等待其他线程运行到某个点的通知。如:Event等。

这两种类型我们常常在开发中结合使用。

Windows有很多只能由内核访问的内核对象,如线程对象,文件对象,当然还有我们要介绍的用于同步控制的内核对象:互斥体(Mutex),信号量(Semaphore),事件(Auto-Reset Event, Manual-Reset Event)等。而内核对象是在内核内存中分配的,因此只有在内核态运行的代码中才能访问到它们,访问他们需要使用windows api,从而需要内核切换,所以使用内核对象会比使用其他的原语需要更高的开销。但是内核对象的优势也非常明显,很多都是用户态的同步机制(Win32临界区或CLR的Monitor等)所无法实现的,比如进程间的同步,对于同步的控制,和执行粒度更低的等待,以及托管代码与非托管代码之间的互操作。而可以简单快速可靠使用的api也是我们常常使用的一个关键。

接下来我们先来说一下互斥体。

对于一般性的数据竞争问题,解决方法之一就是使用互斥体来使共享状态的并发访问串行化。互斥体其实就是构建了一段指令临界域,在这个临界域中,同时只能有一个线程来执行临界域中的指令。CLR中的互斥体实现就是Mutex,它的目的就是构建拥有互斥行为的临界域,保证只有一个线程可以进入这个临界域。而在底层基本都是使用原子的比较交换(CAS)来实现,这需要硬件来提供支持。

Mutex继承自System.Threading.WaitHandle。

下面的例子展示了多线程中使用Mutex来实现同步。

class Program
    {
        static Mutex m = new Mutex();
        static void Main(string[] args)
        {
            Task t1 = new Task(() => CriticalRegion());
            Task t2 = new Task(() => CriticalRegion());
            t1.Start();
            t2.Start();

            Task.WaitAll(t1, t2);
            Console.WriteLine("Finished.");
            Console.ReadLine();
        }

        static void CriticalRegion()
        {
            m.WaitOne();
            try
            {
                //临界区域
                Thread.Sleep(5000);
                //
            }
            finally
            {
                m.ReleaseMutex();
            }
        }
    }

Mutex的WaitOne方法获取成功以后,互斥体将被线程获取并且标记为未触发。除非拥有互斥体的线城市防它并且重新回到未触发状态,否则其他线程都不能获得这个互斥体。释放的方法为ReleaseMutex。

如果有多个线程在等待同一个Mutex,那么内核将通过FIFO算法来跟踪等待着并决定唤醒哪一个线程。虽然在内核中有一定的顺序,但是我们不能保证我们的多线程程序能够按顺序执行WaitOne方法。所以对我们上层应用来说,首先唤醒哪个线程是未知的。但是你可以通过使用线程的优先级来提升线程首先被执行的可能性。

static void TestPriority()
        {
            for (int i = 0; i < 10; i++)
            {
                string taskName = "t" + i;
                Thread t1 = new Thread(() => CriticalRegionWithName(taskName));
 
                if (i == 6)
                {
                    t1.Priority = ThreadPriority.Highest;
                }
                t1.IsBackground = true;
                t1.Start();
            }
        }
 
        static void CriticalRegionWithName(string name)
        {
            m.WaitOne();
            try
            {
                //临界区域
                Thread.Sleep(2000);
                Console.WriteLine(name);
                //
            }
            finally
            {
                m.ReleaseMutex();
            }
        }

互斥体对象支持递归获取,这意味着当拥有互斥体的线程在互斥体上再次进行等待的时候,这个等待将马上被满足,即使对象处于未触发状态。在互斥体内部,内核维护者一个计数器,对于每个互斥体来说,初始值为0,每次执行获取操作WaitOne的时候,都会增加1,而每次释放Release的时候都会减去1。只有这个互斥体的计数器降为0的时候,其他线程才能够重新获取这个互斥体。所以在使用互斥体Mutex的时候一定要记得调用ReleaseMutex释放。

static void TestRecursion()
        {
            Task t1 = new Task(() => CriticalRegionWithRecursion("t1"));
            Task t2 = new Task(() => CriticalRegionWithRecursion("t2"));
            t1.Start();
            t2.Start();
 
            Task.WaitAll(t1, t2);
            Console.WriteLine("Finished.");
            Console.ReadLine();
        }
 
        static void CriticalRegionWithRecursion(string taskName)
        {
            m.WaitOne();
            Console.WriteLine(taskName + " got the mutex");
            try
            {               
                try
                {
                    m.WaitOne();
                    Console.WriteLine(taskName + " got the mutex");
                    //临界区域
                    Thread.Sleep(3000);
                }
                finally
                {
                    m.ReleaseMutex();
                    Console.WriteLine(taskName + " released the mutex");
                }
                //临界区域
                Thread.Sleep(5000);
            }
            finally
            {
                m.ReleaseMutex();
                Console.WriteLine(taskName + " released the mutex");
            }
        }

结果:

image

Mutex还是已有有Owner的锁对象,和Monitor一样。只有获取了带锁的对象才能释放它,如果获取的线程和释放的线程不是同一个线程的话将会产生异常: 从不同步的代码块中调用了对象同步方法。

测试代码:

        static void TestOwner()
        {
            Task.Factory.StartNew(() => { m.WaitOne(); Thread.Sleep(5000); });
            Thread.Sleep(2000);
            Task.Factory.StartNew(() => { m.ReleaseMutex(); });
        }

Mutex可以作为机器范围的,也可以用作进程范围的。在进程范围中,我们可以使用它来同步线程,而在机器范围中,我们可以用Mutex来同步进程。进程同步和线程同步类似,即保证同步的访问在进程间共享的数据。常见的进程间同步是用Mutex来保证进程的在机器上的单实例运行。在内核对象中,同样可以使用与进程间同步的还有Semaphore。

static void Main(string[] args)
        {
            CheckProcessExists();
            Console.ReadLine();
        }

static void CheckProcessExists()
        {
            using (var mutex = new Mutex(false, "Global\\Demo"))
            {
                if (!mutex.WaitOne(TimeSpan.FromSeconds(3), false))
                {
                    Console.WriteLine("Another app instance is running. Bye!");
                    return;
                }
                Console.WriteLine("Runing...");
                Console.ReadLine();
            }
        }

附上MSDN上面的说明 (http://msdn.microsoft.com/zh-cn/library/system.threading.mutex.aspx):

image

拥有该Mutex互斥体的线程在结束之前没有正确的释放Mutex,比如忘记写finally,忘记ReleaseMutex,或者在递归获取Mutex的过程中计数器出现错误,又或者进程或线程突然中止,那么这个Mutex就会被认为是废弃的互斥体。当互斥体被废弃时,如果不借助操作系统的帮助,那么其他的线程将不能获取到该Mutex,因为此时互斥体并没有被释放。也不用太担心,操作系统会作处理,操作系统可以保证党出现废弃的互斥体时候,有一个等待线程可以被唤醒获取到该互斥体。

Mutex互斥体由于是内核对象,在使用过程中会有大量的内核切换,所以它的效率不是很高。执行的速度约比lock(Monitor)锁慢50倍左右。所以在我们多线程编程中,Mutex的使用频率并不是很高。

测试代码在这里下载

 

.Net中多线程编程的基本概念和详细使用说明 线程和进程最大的一个区别就在于线程间可以共享数据和资源,而进程则充分地隔离。在很多场合,即使同一进程的多个线程之间拥有相同的内存空间,也需要在逻辑上为某些线程分配独享的数据。例如,在实际开发中往往会针对一些ORM如EF一类的上下文实体做线程内唯一实例的设置,这时就需要用到下面提到的技术。(1)线程本地存储(Thread Local Storage,TLS) 阅读详情

相关推荐

c++protobuf使用注意事项--1、不能在c++clr中使用

因为mutex不能用于clr。 如果要用就要通过net接口

鸟哥的博客 489

c# 多应用程序池 如何使用Mutex共享数据?

Mark,jiagoxinzhi,linyee c# 多应用程序池 如何共享数据? 先建个文章,,以后再补解决方案,,真实解决方案还没有找到。 使用MMF共享数据,使用Mutex防止访问MMF冲突 1、Mutex 多进程多线程锁,用Winform测试时正常,移植到站点时,测试发现  一个站点正常,一个站抛出 拒绝访问 2、经查应该是用户不同导致无法共享,之后查看w3wp.exe进程的用户...

Linyee编程专栏 571

11、Windows 系统中的同步机制:从内核对象到用户模式抽象

本文深入探讨了Windows系统中的同步机制,涵盖从内核对象到用户模式下的各种同步抽象。详细介绍了Win32和.NET Framework中的常见同步原语,包括CRITICAL_SECTION、CLR锁、SRWL、ReaderWriterLockSlim、条件变量等,分析了它们的原理、使用方法及性能特点。旨在帮助开发者理解并合理运用这些机制,以构建高效、稳定的并发程序。

bert9linguist的博客 70

【CLR】C#线程同步和锁-----看这篇就够了

多个线程同时访问共享数据的时候,线程同步能够防止线程损坏。之所以强调同时,是因为线程同步问题其实就是访问时间问题。如果有些数据会被多个线程访问,但是这几个线程访问的时间都是错开的,不会同时接触到数据,那完全就用不到线程同步。 线程同步会遇到几个问题,所以能避免使用就别用: 使用繁琐,容易出错:你需要清楚的明白哪些数据可能是会被同时访问的,然后用一个锁锁住它,确保一次只有一个线程能够访问。如果任何一块数据被漏了,那就会有问题。 没法确定你所有用锁的方式一定正确:只能靠测试和经验。 损害性能:获取和释放锁时要

catshitone的专栏 5013

内存映射与多线程同步机制在.NET中的应用

本文详细探讨了.NET中内存映射文件(MemoryMappedFile)的使用方法,以及如何通过互斥锁(Mutex)和事件(AutoResetEvent)实现线程间的通信和同步。此外,文章还介绍了ReaderWriterLockSlim类的使用,这是.NET中一个用于改进读写锁性能和可伸缩性的工具,特别适用于多线程环境下的资源访问控制。

weixin_29323977的博客 367

操作系统同步和互斥(生产者和消费者问题)

操作系统线程同步和互斥,生产者和消费者问题例子,VC++编写

C#.Net学习笔记——CLR核心机制

的运行环境。

weixin_46711336的博客 3801

C#.NET多线程实例6个(包括多线程基本使用,多线程互斥等全部多线程使用实例)(十分经典)

C#.NET多线程实例6个(包括多线程基本使用,多线程互斥等全部多线程使用实例)(十分经典)

.Net CLR 中的同步机制(一): 互斥体

随着软硬件技术的发展,无论是在Web服务或者云计算,还是单一的应用程序,串行方式编写的软件越来越少,我们总是可以看见并行的存在。但是并行并不是适合于每一种场景,也完全不是将工作扔到线程池中排队运行那么简单。 由于在进程中,多个线程可能需要访问相同的虚拟内存地址空间,如果不进行控制就很容易出现数据竞争的并发问题,大多是因为操作非原子性和线程时间片的原因引起的,导致的现象会是抛出异常,程序崩溃,数据...

weixin_30819163的博客 209

C#.NET多线程实例6个(包括多线程基本使用,多线程互斥等全部多线程使用实例)

C#.NET多线程实例6个(包括多线程基本使用,多线程互斥等全部多线程使用实例)

C#.NET多线程实例6个(包括多线程基本使用,多线程互斥等全部多线程使用实例)(201903)

C#.NET多线程实例6个(包括多线程基本使用,多线程互斥等全部多线程使用实例)(201903)

在 C# 中,Monitor 是一种托管的线程同步机制,由 CLR(公共语言运行时)实现,用于协调线程的执行顺序和保护共享资源

结合之前的讨论(上下文切换、共享资源保护、线程池优化、异步锁等),以下是对 Monitor 托管实现及其协调线程执行顺序的详细解析,包括核心概念、实现细节、与 SpinLock、SemaphoreSlim 等机制的对比、示例代码、最佳实践,以及常见问题和解决方案。Monitor 是协调线程执行顺序的强大工具,适合同步场景,但在异步编程中需谨慎使用。在 C# 中,Monitor 是一种托管的线程同步机制,由 CLR(公共语言运行时)实现,用于协调线程的执行顺序和保护共享资源。3.3 与异步编程的对比。

专注于前沿技术的探索与实践,热衷于从代码逻辑、架构设计到行业应用等多维度深挖技术本质。期待在技术之路上与你共同成长~ 586

ADS1220 STM32硬件SPI驱动程序

前一段时间温故了下标准库,使用的是F1系列芯片,测试程序。

随风飘零翼的博客 5767

C#.NET Monitor 与 Mutex 深入解析:进程内同步、跨进程互斥与使用边界

在.NET里提到同步,很多人第一反应通常是:lock (_gate) { // 临界区 }这没问题。但只要你继续往下挖,很快就会碰到两个更底层的名字:它们都能做“互斥”,但解决的问题并不是同一类。Monitor是进程内线程同步的默认基础设施,Mutex则更偏跨进程互斥的操作系统级工具。lock和Monitor到底是什么关系;真正解决什么问题;Mutex为什么不能简单理解成“更高级的锁”;什么时候该用Monitor,什么时候才值得上Mutex;它们和的边界是什么。

2401_89221445的博客 341

Jlink除了调试还能干啥?用JFlash SPI给W25Q64烧录固件的5个实用技巧

本文深入探讨了Jlink的隐藏功能,特别是通过JFlash SPI高效烧录W25Q64的5个进阶技巧。从硬件连接优化到批量处理脚本,再到数据安全验证,这些技巧能显著提升烧录速度和稳定性,适用于量产环境。文章还提供了详细的参数配置和脚本示例,帮助工程师充分发挥Jlink的潜力。

weixin_29164843的博客 409

京东商品数据爬取实战:用Selenium+代理IP绕过最新反爬机制(附完整代码)

本文深入解析京东商品数据采集技术,详细介绍了如何利用Selenium与智能反反爬策略绕过京东最新的反爬机制。通过实战案例和完整代码,帮助开发者掌握高效采集京东商品数据的方法,包括动态参数加密、行为验证识别及分布式采集架构设计等关键技术。

cc789的博客 1060

讨论并发编程中的内存模型,以及它与线程同步和互斥的关系

定义了程序在多线程环境下如何访问和共享内存数据。它描述了线程对变量的读写操作如何在硬件和编译器优化下表现出来,以及线程之间如何实现数据的可见性和一致性。线程同步的目标是解决内存模型中的可见性和有序性问题,确保线程之间对共享数据的访问是安全且一致的。通过理解内存模型及其与线程同步和互斥的关系,开发者可以编写更高效且可靠的多线程程序。是确保共享资源一次只能被一个线程访问的机制,它直接影响内存模型的可见性和有序性。C# 基于 .NET 平台,遵循。

fapw15的博客 725

SAP CRP能力计划详细说明操作及相关配置

2、求助朋友同事的希望破灭后,网上找了资料发现在百度搜索的资料基本都是没有说明白什么才产能计划,都没有详细的操作说明,然后用google去搜索了一些相关的文章,好像也没有详细的文章。然后也去SAP的官网搜索了相关的文章,都是文字性的表述,对系统的配置都没有很大的帮助,很久之前有记得在隐身人的博客中有提到CRP的能力计划的文章,然后试着去找了一下相关的文章。5、首先产能排产肯定涉及的就是是能力的问题,能力第一个能想到的就是工作中心中的能力页签,在流程制造中也叫资源,其实都是一个东西。

我小时候很黑的博客 4518

.Net学习难点讨论系列5 – 线程同步问题之二

<br />接上篇文章,来说一下托管代码包装Windows内核对象完成线程同步的方法 <br />Windows提供的用于同步的内核对象包括:互斥体、信号量和事件。CLR中System.Threading命名空间下的WaitHandle 类(抽象基类)完成了对这些内核对象的包装,包装内核对象在托管方式下实现同步的类也自然都继承自WaitHandle类,包括 Mutex,Semaphore与EventWaitHandle(基类)。以上提到的这四个类都覆写了WaitOne()方法并实现了 WaitAll(

pp_zz的专栏 338

手把手入门YOLO到改进优化教程专栏介绍与说明——列表目录查阅(持续更新)

手把手入门YOLO到改进优化教程专栏介绍与说明——列表目录查阅(持续更新)

嵌入式阿齐Archie 2万+

基于逆向工程的中国裁判文书网爬虫技术深度解析:从反爬破解到合规抓取

最近承接了一个法律大数据分析的项目,需要获取某类合同纠纷的裁判文书数据,用于分析不同地区的判决规律、法官的裁判倾向以及法条适用情况。原本以为用常规的Python爬虫就能搞定,结果刚对着中国裁判文书网发了几个请求,就遭遇了“滑铁卢”:要么请求直接返回403 Forbidden,要么触发滑块验证码,甚至连IP都被临时封禁了。普通的requests+BeautifulSoup组合在裁判文书网面前完全失效,这也让我意识到,想要获取这里的数据,必须先通过逆向工程拆解网站的请求逻辑和反爬机制。经过一周的摸索,从抓包分析

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 3170

我来详细讲解 .NET 中的 条件等待机制,重点分析其核心概念、工作原理、实现方式(以 Monitor 的 Wait/Pulse/PulseAll 为例)、与其他同步机制的对比、适用场景、常见问题

我来详细讲解 .NET 中的 条件等待机制,重点分析其核心概念、工作原理、实现方式(以 Monitor 的 Wait/Pulse/PulseAll 为例)、与其他同步机制的对比、适用场景、常见问题及最佳实践,并提供完整的代码示例和测试用例。二、条件等待机制的工作原理在 .NET 中,Monitor 是条件等待机制的核心实现,基于对象的同步块(sync block)管理锁和等待队列。条件等待机制 是一种线程同步模式,允许线程在特定条件未满足时暂停执行(等待),并在条件满足时被其他线程唤醒(通知)。

专注于前沿技术的探索与实践,热衷于从代码逻辑、架构设计到行业应用等多维度深挖技术本质。期待在技术之路上与你共同成长~ 924

混合精度训练与推理:平衡性能与效率

在深度学习领域,模型的训练和推理通常需要大量的计算资源和时间。随着模型规模的不断增大,如大型语言模型、复杂的视觉模型等,对计算资源的需求也呈指数级增长。混合精度训练与推理技术应运而生,它通过在训练和推理过程中同时使用不同精度的数据类型,在保证模型性能的前提下,显著提高计算效率,减少内存占用。本文的目的是全面介绍混合精度训练与推理技术,包括其原理、实现方法、应用场景以及相关工具和资源,帮助读者深入理解并掌握该技术,从而在实际项目中更好地平衡性能与效率。

操作系统内核探秘的博客 1068
上一篇: [c#]BPlusTreePrototype - B+树的C#实现
下一篇: C# 两种方法实现HTTP协议迷你服务器
ArvinStudy
博客等级 码龄15年 83粉丝 193原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值