返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C#多线程系列之多线程锁lock和Monitor
  • 930
分享到

C#多线程系列之多线程锁lock和Monitor

2024-04-02 19:04:59 930人浏览 安东尼
摘要

目录1,Locklock 原型lock 编写实例2,Monitor怎么用呢解释一下示例设置获取锁的时效1,Lock lock 用于读一个引用类型进行加锁,同一时刻内只有一个线程能够访

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的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

本文标题: C#多线程系列之多线程锁lock和Monitor

本文链接: https://lsjlt.com/news/138985.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C#多线程系列之多线程锁lock和Monitor
    目录1,Locklock 原型lock 编写实例2,Monitor怎么用呢解释一下示例设置获取锁的时效1,Lock lock 用于读一个引用类型进行加锁,同一时刻内只有一个线程能够访...
    99+
    2024-04-02
  • C#多线程锁lock和Monitor怎么用
    本文小编为大家详细介绍“C#多线程锁lock和Monitor怎么用”,内容详细,步骤清晰,细节处理妥当,希望这篇“C#多线程锁lock和Monitor怎么用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1,Loc...
    99+
    2023-06-29
  • C#多线程系列之读写锁
    本篇的内容主要是介绍 ReaderWriterLockSlim 类,来实现多线程下的读写分离。 ReaderWriterLockSlim ReaderWriterLock 类:定义支...
    99+
    2024-04-02
  • C#多线程之线程锁
    目录一、Mutex类二、Mutex的用途三、Semaphore信号量1、简介2、初始化3、WaitOne()和Release()四、Monitor类典型的生产者与消费者实例五、Loc...
    99+
    2024-04-02
  • C#多线程系列之线程池
    目录线程池ThreadPool 常用属性和方法线程池说明和示例线程池线程数线程池线程数说明不支持的线程池异步委托任务取消功能计时器线程池 线程池全称为托管线程池,线程池受 .NET ...
    99+
    2024-04-02
  • C#多线程系列之线程通知
    AutoRestEvent 类用于从一个线程向另一个线程发送通知。 微软文档是这样介绍的:表示线程同步事件在一个等待线程释放后收到信号时自动重置。 其构造函数只有一个: 构造函数里面...
    99+
    2024-04-02
  • C#多线程系列之线程等待
    目录前言volatile 关键字三种常用等待再说自旋和阻塞SpinWait 结构属性和方法自旋示例新的实现SpinLock 结构属性和方法示例等待性能对比前言 volatile 关键...
    99+
    2024-04-02
  • C#多线程系列之多阶段并行线程
    前言 这一篇,我们将学习用于实现并行任务、使得多个线程有序同步完成多个阶段的任务。 应用场景主要是控制 N 个线程(可随时增加或减少执行的线程),使得多线程在能够在 M 个阶段中保持...
    99+
    2024-04-02
  • C#多线程系列之线程完成数
    解决一个问题 假如,程序需要向一个 Web 发送 5 次请求,受网路波动影响,有一定几率请求失败。如果失败了,就需要重试。 示例代码如下: class Program ...
    99+
    2024-04-02
  • C#多线程系列之手动线程通知
    区别与示例 AutoResetEvent 和 ManualResetEvent 十分相似。两者之间的区别,在于前者是自动(Auto),后者是手动(Manua)。 你可以先运行下面的示...
    99+
    2024-04-02
  • Java多线程之同步锁-lock详解
    目录一、题目描述二、解题思路三、代码详解一、题目描述 题目: 同步锁出现的目的就是为了解决多线程安全问题。 同步锁的几种方式 synchronized 1、同步代码块 2、同步方法 ...
    99+
    2024-04-02
  • Java多线程之多种锁和阻塞队列
    目录一、悲观锁和乐观锁1.1. 乐观锁1.2. 悲观锁二、公平锁和非公平锁三、可重入锁(递归锁)四、自旋锁五、独占锁(写)/共享锁(读)六、什么是阻塞队列?七、阻塞队列(Blocki...
    99+
    2024-04-02
  • C#多线程之线程锁实例分析
    这篇文章主要介绍了C#多线程之线程锁实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C#多线程之线程锁实例分析文章都会有所收获,下面我们一起来看看吧。一、Mutex类“mutex”是术语“互相排斥(mut...
    99+
    2023-06-30
  • C#多线程系列之线程的创建和生命周期
    目录1,获取当前线程信息2,管理线程状态2.1启动与参数传递2.1.1ParameterizedThreadStart2.1.2使用静态变量或类成员变量2.1.3委托与Lambda2...
    99+
    2024-04-02
  • C#多线程系列之原子操作
    目录知识点竞争条件线程同步CPU时间片和上下文切换阻塞内核模式和用户模式Interlocked类1,出现问题2,Interlocked.Increment()3,Interlocke...
    99+
    2024-04-02
  • C++多线程之互斥锁与死锁
    目录1.前言2.互斥锁2.1 互斥锁的特点2.2 互斥锁的使用2.3 std::lock_guard3.死锁3.1 死锁的含义3.2 死锁的例子3.3 死锁的解决方法1.前言 比如说...
    99+
    2024-04-02
  • C#多线程系列之进程同步Mutex类
    Mutex 中文为互斥,Mutex 类叫做互斥锁。它还可用于进程间同步的同步基元。 Mutex 跟 lock 相似,但是 Mutex 支持多个进程。Mutex 大约比 lock 慢 ...
    99+
    2024-04-02
  • C#多线程系列之async和await用法详解
    目录async和awaitasyncawait从以往知识推导创建异步任务创建异步任务并返回Task异步改同步说说 await Task说说 async Task<TR...
    99+
    2024-04-02
  • C#多线程中lock怎么用
    这篇文章主要介绍了C#多线程中lock怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。下面就是一段简单的代码。public class AccessC...
    99+
    2023-06-17
  • C#多线程之线程同步
    一、前言 我们先来看下面一个例子: using System; using System.Threading; namespace ThreadSynchDemo { cl...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作