千家信息网

C#多线程锁lock和Monitor怎么用

发表于:2025-11-07 作者:千家信息网编辑
千家信息网最后更新 2025年11月07日,本文小编为大家详细介绍"C#多线程锁lock和Monitor怎么用",内容详细,步骤清晰,细节处理妥当,希望这篇"C#多线程锁lock和Monitor怎么用"文章能帮助大家解决疑惑,下面跟着小编的思路
千家信息网最后更新 2025年11月07日C#多线程锁lock和Monitor怎么用

本文小编为大家详细介绍"C#多线程锁lock和Monitor怎么用",内容详细,步骤清晰,细节处理妥当,希望这篇"C#多线程锁lock和Monitor怎么用"文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

1,Lock

lock 用于读一个引用类型进行加锁,同一时刻内只有一个线程能够访问此对象。lock 是语法糖,是通过 Monitor 来实现的。

Lock 锁定的对象,应该是静态的引用类型(字符串除外)。

实际上字符串也可以作为锁的对象使用,只是由于字符串对象的特殊性,可能会造成不同位置的不同线程冲突。
如果你能保证字符串的唯一性,例如 Guid 生成的字符串,也是可以作为锁的对象使用的(但不建议)。
锁的对象也不一定要静态才行,也可以通过类实例的成员变量,作为锁对象。

lock 原型

lock 是 Monitor 的语法糖,生成的代码对比:

lock (x){    // Your code...}
object __lockObj = x;bool __lockWasTaken = false;try{    System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);    // Your code...}finally{    if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);}

这里先不理会 Monitor,后面再说。

lock 编写实例

首先,如果像下面这样写的话,拉出去打 si 吧。

        public void MyLock()        {            object o = new object();            lock (o)            {            //             }        }

下面编写一个简单的锁,示例如下:

    class Program    {        private static object obj = new object();        private static int sum = 0;        static void Main(string[] args)        {            Thread thread1 = new Thread(Sum1);            thread1.Start();            Thread thread2 = new Thread(Sum2);            thread2.Start();            while (true)            {                Console.WriteLine($"{DateTime.Now.ToString()}:" + sum);                Thread.Sleep(TimeSpan.FromSeconds(1));            }        }        public static void Sum1()        {            sum = 0;            lock (obj)            {                for (int i = 0; i < 10; i++)                {                    sum += i;                    Console.WriteLine("Sum1");                    Thread.Sleep(TimeSpan.FromSeconds(2));                }            }        }        public static void Sum2()        {            sum = 0;            lock (obj)            {                for (int i = 0; i < 10; i++)                {                    sum += 1;                    Console.WriteLine("Sum2");                    Thread.Sleep(TimeSpan.FromSeconds(2));                }            }        }    }

类将自己设置为锁, 这可以防止恶意代码对公共对象采用做锁。

例如:

  public void Access()    {        lock(this) {}     }

锁可以阻止其它线程执行锁块(lock(o){})中的代码,当锁定时,其它线程必须等待锁中的线程执行完成并释放锁。但是这可能会给程序带来性能影响。
锁不太适合I/O场景,例如文件I/O,繁杂的计算或者操作比较持久的过程,会给程序带来很大的性能损失。

10 种优化锁的性能方法: http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/

2,Monitor

此对象提供同步访问对象的机制;Monotor 是一个静态类型,其方法比较少,常用方法如下:

操作说明
Enter, TryEnter获取对象的锁。 此操作还标记关键节的开头。 其他任何线程都不能输入临界区,除非它使用不同的锁定对象执行临界区中的说明。
Wait释放对象的锁,以允许其他线程锁定并访问对象。 调用线程会等待另一个线程访问对象。 使用脉冲信号通知等待线程关于对象状态的更改。
Pulse 、PulseAll将信号发送到一个或多个等待线程。 信号通知等待线程:锁定对象的状态已更改,锁的所有者已准备好释放该锁。 正在等待的线程置于对象的就绪队列中,因此它可能最终接收对象的锁。 线程锁定后,它可以检查对象的新状态,以查看是否已达到所需的状态。
Exit释放对象的锁。 此操作还标记受锁定对象保护的临界区的结尾。

怎么用呢

下面是一个很简单的示例:

        private static object obj = new object();        private static bool acquiredLock = false;                                public static void Test()        {            try            {                Monitor.Enter(obj, ref acquiredLock);            }            catch { }            finally            {                if (acquiredLock)                    Monitor.Exit(obj);            }        }

Monitor.Enter 锁定 obj 这个对象,并且设置 acquiredLock 为 true,告诉别人 obj 已经被锁定。

最后结束时,判断 acquiredLock ,释放锁,并设置 acquiredLock 为 false。

解释一下

临界区:指被某些符号包围的范围。例如 {} 内。

Monitor 对象的 Enter 和 Exit 方法来标记临界区的开头和结尾。

Enter() 方法获取锁后,能够保证只有单个线程能够使用临界区中的代码。使用 Monitor 类,最好搭配 try{...}catch{...}finally{...} 来使用,因为如果获取到锁但是没有释放锁的话,会导致其它线程无限阻塞,即发生死锁。

一般来说,lock 关键字够用了。

示例

下面示范了多个线程如何使用 Monitor 来实现锁:

       private static object obj = new object();        private static bool acquiredLock = false;        static void Main(string[] args)        {            new Thread(Test1).Start();            Thread.Sleep(1000);            new Thread(Test2).Start();        }        public static void Test1()        {            try            {                Monitor.Enter(obj, ref acquiredLock);                for (int i = 0; i < 10; i++)                {                    Console.WriteLine("Test1正在锁定资源");                    Thread.Sleep(1000);                }            }            catch { }            finally            {                if (acquiredLock)                    Monitor.Exit(obj);                Console.WriteLine("Test1已经释放资源");            }        }        public static void Test2()        {            bool isGetLock = false;            Monitor.Enter(obj);            try            {                Monitor.Enter(obj, ref acquiredLock);                for (int i = 0; i < 10; i++)                {                    Console.WriteLine("Test2正在锁定资源");                    Thread.Sleep(1000);                }            }            catch { }            finally            {                if (acquiredLock)                    Monitor.Exit(obj);                Console.WriteLine("Test2已经释放资源");            }        }

设置获取锁的时效

如果对象已经被锁定,另一个线程使用 Monitor.Enter 对象,就会一直等待另一个线程解除锁定。

但是,如果一个线程发生问题或者出现死锁的情况,锁一直被锁定呢?或者线程具有时效性,超过一段时间不执行,已经没有了意义呢?

我们可以通过 Monitor.TryEnter() 来设置等待时间,超过一段时间后,如果锁还没有释放,就会返回 false。

改造上面的示例如下:

        private static object obj = new object();        private static bool acquiredLock = false;        static void Main(string[] args)        {            new Thread(Test1).Start();            Thread.Sleep(1000);            new Thread(Test2).Start();        }        public static void Test1()        {            try            {                Monitor.Enter(obj, ref acquiredLock);                for (int i = 0; i < 10; i++)                {                    Console.WriteLine("Test1正在锁定资源");                    Thread.Sleep(1000);                }            }            catch { }            finally            {                if (acquiredLock)                    Monitor.Exit(obj);                Console.WriteLine("Test1已经释放资源");            }        }        public static void Test2()        {            bool isGetLock = false;            isGetLock = Monitor.TryEnter(obj, 500);            if (isGetLock == false)            {                Console.WriteLine("锁还没有释放,我不干活了");                return;            }            try            {                Monitor.Enter(obj, ref acquiredLock);                for (int i = 0; i < 10; i++)                {                    Console.WriteLine("Test2正在锁定资源");                    Thread.Sleep(1000);                }            }            catch { }            finally            {                if (acquiredLock)                    Monitor.Exit(obj);                Console.WriteLine("Test2已经释放资源");            }        }

读到这里,这篇"C#多线程锁lock和Monitor怎么用"文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注行业资讯频道。

对象 线程 资源 字符 字符串 方法 正在 代码 状态 示例 C# 不同 信号 性能 文章 时间 标记 类型 静态 关键 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 数据分析软件开发实例 数据库建立索引能不能修改记录 数据库安全参考书 趣播app的直播服务器 学数据库需要什么配置电脑 盗贼之海微软连不到服务器 甘泉县司法局网络安全 重庆大学数据库研究生 苏州计算机软件开发需要多少钱 无线网络安全设置怎么设置 绍兴隆信网络技术有限公司 上虞 华为网络安全实验室搭建 idea下载数据库连接包 如何将云服务器的图片保存到本地 光网络技术课程报告 信息系统与数据库是什么 财务软件开发c 数据库泄密困境 软件开发公司的行情 图片怎么存放到数据库 检测http服务器网络是否正常 地图数据的采集和地图数据库 教学网络安全视频 提示无法连接服务器是怎么回事 国内收费代理服务器 会计好还是计算机网络技术好 广东正规软件开发服务费 软件开发公司好做吗 初中文化网络技术学点什么好 根据网络安全法国家采取措施
0