iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C#使用读写锁解决多线程并发问题
  • 683
分享到

C#使用读写锁解决多线程并发问题

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

一、简介 在开发程序的过程中,难免少不了写入错误日志这个关键功能。实现这个功能,可以选择使用第三方日志插件,也可以选择使用数据库,还可以自己写个简单的方法把错误信息记录到日志文件。现

一、简介

开发程序的过程中,难免少不了写入错误日志这个关键功能。实现这个功能,可以选择使用第三方日志插件,也可以选择使用数据库,还可以自己写个简单的方法把错误信息记录到日志文件。现在我们来讲下最后一种方法:

在选择最后一种方法实现的时候,若对文件操作与线程同步不熟悉,问题就有可能出现了,因为同一个文件并不允许多个线程同时写入,否则会提示“文件正在由另一进程使用,因此该进程无法访问此文件”。这是文件的并发写入问题,就需要用到线程同步。而微软也给线程同步提供了一些相关的类可以达到这样的目的,本文使用到的 System.Threading.ReaderWriterLockSlim 便是其中之一。该类用于管理资源访问的定状态,可实现多线程读取或进行独占式写入访问。利用这个类,我们就可以避免在同一时间段内多线程同时写入一个文件而导致的并发写入问题。读写锁是以 ReaderWriterLockSlim 对象作为锁管理资源的,不同的 ReaderWriterLockSlim 对象中锁定同一个文件也会被视为不同的锁进行管理,这种差异可能会再次导致文件的并发写入问题,所以 ReaderWriterLockSlim 应尽量定义为只读的静态对象。
ReaderWriterLockSlim 有几个关键的方法,本文仅讨论写入锁:

1.调用 EnterWriteLock 方法 进入写入状态,在调用线程进入锁定状态之前一直处于阻塞状态,因此可能永远都不返回。
2.调用 TryEnterWriteLock 方法 进入写入状态,可指定阻塞的间隔时间,如果调用线程在此间隔期间并未进入写入模式,将返回false。
3.调用 ExitWriteLock 方法 退出写入状态,应使用 finally 块执行 ExitWriteLock 方法,从而确保调用方退出写入模式。

二、不使用读写锁写入文件:

代码:

class Program
    {
        static int LoGCount = 100;
        static int WritedCount = 0;
        static int FailedCount = 0;
        static void Main(string[] args)
        {
            //迭代运行写入日志记录,由于多个线程同时写入同一个文件将会导致错误
            Parallel.For(0, LogCount, e =>
            {
                WriteLog1();
            });
            Console.WriteLine(string.FORMat("\r\nLog Count:{0}.\t\tWrited Count:{1}.\tFailed Count:{2}.", LogCount.ToString(), WritedCount.ToString(), FailedCount.ToString()));
            Console.Read();
        }
        #region 未加入读写锁
        //不使用读写锁写入文件
        static void WriteLog1()
        {
            try
            {
                var logFilePath = "log.txt";
                var now = DateTime.Now;
                var logContent = string.Format("Tid: {0}{1} {2}.{3}\r\n", Thread.CurrentThread.ManagedThreadId.ToString().PadRight(4), now.ToLongDateString(), now.ToLongTimeString(), now.Millisecond.ToString());
                File.AppendAllText(logFilePath, logContent);
                WritedCount++;
            }
            catch (Exception ex)
            {
                FailedCount++;
                Console.WriteLine(ex.Message);
            }
        }
        #endregion
    }

运行结果:

不是所有的log都能写入到log.txt,因为不适用读写错可能会出现上面提到的:“文件正在由另一进程使用,因此该进程无法访问此文件”报错信息。

记录log信息:

Tid: 9   2021年5月21日 下午 02:18:04.919
Tid: 9   2021年5月21日 下午 02:18:04.944
Tid: 9   2021年5月21日 下午 02:18:05.80
Tid: 11  2021年5月21日 下午 02:18:05.81
Tid: 9   2021年5月21日 下午 02:18:05.82
Tid: 12  2021年5月21日 下午 02:18:05.83
Tid: 11  2021年5月21日 下午 02:18:05.84
Tid: 12  2021年5月21日 下午 02:18:05.84
Tid: 16  2021年5月21日 下午 02:18:05.85
Tid: 12  2021年5月21日 下午 02:18:05.111
Tid: 16  2021年5月21日 下午 02:18:05.117
Tid: 16  2021年5月21日 下午 02:18:05.128
Tid: 11  2021年5月21日 下午 02:18:05.128
Tid: 16  2021年5月21日 下午 02:18:05.133
Tid: 12  2021年5月21日 下午 02:18:05.138
Tid: 16  2021年5月21日 下午 02:18:05.140
Tid: 12  2021年5月21日 下午 02:18:05.140
Tid: 16  2021年5月21日 下午 02:18:05.142
Tid: 16  2021年5月21日 下午 02:18:05.144
Tid: 16  2021年5月21日 下午 02:18:05.151
Tid: 16  2021年5月21日 下午 02:18:05.158
Tid: 9   2021年5月21日 下午 02:18:05.159
Tid: 10  2021年5月21日 下午 02:18:05.159
Tid: 9   2021年5月21日 下午 02:18:05.164
Tid: 16  2021年5月21日 下午 02:18:05.164
Tid: 9   2021年5月21日 下午 02:18:05.172
Tid: 15  2021年5月21日 下午 02:18:05.172
Tid: 16  2021年5月21日 下午 02:18:05.181
Tid: 16  2021年5月21日 下午 02:18:05.187
Tid: 15  2021年5月21日 下午 02:18:05.188
Tid: 16  2021年5月21日 下午 02:18:05.195
Tid: 16  2021年5月21日 下午 02:18:05.196
Tid: 15  2021年5月21日 下午 02:18:05.195
Tid: 16  2021年5月21日 下午 02:18:05.202
Tid: 16  2021年5月21日 下午 02:18:05.203
Tid: 15  2021年5月21日 下午 02:18:05.202
Tid: 15  2021年5月21日 下午 02:18:05.207
Tid: 15  2021年5月21日 下午 02:18:05.209
Tid: 16  2021年5月21日 下午 02:18:05.207
Tid: 15  2021年5月21日 下午 02:18:05.210
Tid: 15  2021年5月21日 下午 02:18:05.222
Tid: 15  2021年5月21日 下午 02:18:05.231
Tid: 18  2021年5月21日 下午 02:18:05.238
Tid: 15  2021年5月21日 下午 02:18:05.238
Tid: 18  2021年5月21日 下午 02:18:05.244
Tid: 15  2021年5月21日 下午 02:18:05.251
Tid: 15  2021年5月21日 下午 02:18:05.256
Tid: 15  2021年5月21日 下午 02:18:05.262
Tid: 15  2021年5月21日 下午 02:18:05.304
Tid: 15  2021年5月21日 下午 02:18:05.312
Tid: 13  2021年5月21日 下午 02:18:05.312
Tid: 9   2021年5月21日 下午 02:18:05.313
Tid: 13  2021年5月21日 下午 02:18:05.320
Tid: 19  2021年5月21日 下午 02:18:05.320
Tid: 16  2021年5月21日 下午 02:18:05.325
Tid: 19  2021年5月21日 下午 02:18:05.333
Tid: 16  2021年5月21日 下午 02:18:05.342
Tid: 16  2021年5月21日 下午 02:18:05.349
Tid: 16  2021年5月21日 下午 02:18:05.361
Tid: 16  2021年5月21日 下午 02:18:05.366
Tid: 16  2021年5月21日 下午 02:18:05.367
Tid: 16  2021年5月21日 下午 02:18:05.368
Tid: 16  2021年5月21日 下午 02:18:05.376
Tid: 16  2021年5月21日 下午 02:18:05.386
Tid: 16  2021年5月21日 下午 02:18:05.392
Tid: 16  2021年5月21日 下午 02:18:05.401
Tid: 9   2021年5月21日 下午 02:18:05.463
Tid: 13  2021年5月21日 下午 02:18:05.464
Tid: 15  2021年5月21日 下午 02:18:05.464
Tid: 13  2021年5月21日 下午 02:18:05.465
Tid: 13  2021年5月21日 下午 02:18:05.470
Tid: 11  2021年5月21日 下午 02:18:05.479

三、使用读写锁写入文件:

代码:

class Program
    {
        static int LogCount = 100;
        static int WritedCount = 0;
        static int FailedCount = 0;
        static void Main(string[] args)
        {
            //迭代运行写入日志记录,由于多个线程同时写入同一个文件将会导致错误
            Parallel.For(0, LogCount, e =>
            {
                WriteLog2();
            });
            Console.WriteLine(string.Format("\r\nLog Count:{0}.\t\tWrited Count:{1}.\tFailed Count:{2}.", LogCount.ToString(), WritedCount.ToString(), FailedCount.ToString()));
            Console.Read();
        }
        #region 加入读写锁
        //读写锁,当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入
        static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
        static void WriteLog2()
        {
            try
            {
                //设置读写锁为写入模式独占资源,其他写入请求需要等待本次写入结束之后才能继续写入
                //注意:长时间持有读线程锁或写线程锁会使其他线程发生饥饿 (starve)。 为了得到最好的性能,需要考虑重新构造应用程序以将写访问的持续时间减少到最小。
                //从性能方面考虑,请求进入写入模式应该紧跟文件操作之前,在此处进入写入模式仅是为了降低代码复杂度
                //因进入与退出写入模式应在同一个try finally语句块内,所以在请求进入写入模式之前不能触发异常,否则释放次数大于请求次数将会触发异常
                LogWriteLock.EnterWriteLock();
                var logFilePath = "log.txt";
                var now = DateTime.Now;
                var logContent = string.Format("Tid: {0}{1} {2}.{3}\r\n", Thread.CurrentThread.ManagedThreadId.ToString().PadRight(4), now.ToLongDateString(), now.ToLongTimeString(), now.Millisecond.ToString());

                File.AppendAllText(logFilePath, logContent);
                WritedCount++;
            }
            catch (Exception)
            {
                FailedCount++;
            }
            finally
            {
                //退出写入模式,释放资源占用
                //注意:一次请求对应一次释放
                //若释放次数大于请求次数将会触发异常[写入锁定未经保持即被释放]
                //若请求处理完成后未释放将会触发异常[此模式不下允许以递归方式获取写入锁定]
                LogWriteLock.ExitWriteLock();
            }
        }
        #endregion
    }

运行结果:

所有的log都完全正确写入到log.txt。

记录log信息:

Tid: 8   2021年5月21日 下午 02:26:36.573
Tid: 8   2021年5月21日 下午 02:26:36.597
Tid: 8   2021年5月21日 下午 02:26:36.599
Tid: 8   2021年5月21日 下午 02:26:36.600
Tid: 8   2021年5月21日 下午 02:26:36.601
Tid: 8   2021年5月21日 下午 02:26:36.602
Tid: 8   2021年5月21日 下午 02:26:36.608
Tid: 8   2021年5月21日 下午 02:26:36.609
Tid: 8   2021年5月21日 下午 02:26:36.614
Tid: 8   2021年5月21日 下午 02:26:36.616
Tid: 8   2021年5月21日 下午 02:26:36.617
Tid: 8   2021年5月21日 下午 02:26:36.620
Tid: 8   2021年5月21日 下午 02:26:36.620
Tid: 8   2021年5月21日 下午 02:26:36.621
Tid: 8   2021年5月21日 下午 02:26:36.622
Tid: 8   2021年5月21日 下午 02:26:36.623
Tid: 8   2021年5月21日 下午 02:26:36.624
Tid: 8   2021年5月21日 下午 02:26:36.624
Tid: 8   2021年5月21日 下午 02:26:36.625
Tid: 8   2021年5月21日 下午 02:26:36.626
Tid: 8   2021年5月21日 下午 02:26:36.626
Tid: 8   2021年5月21日 下午 02:26:36.627
Tid: 8   2021年5月21日 下午 02:26:36.628
Tid: 8   2021年5月21日 下午 02:26:36.628
Tid: 8   2021年5月21日 下午 02:26:36.629
Tid: 8   2021年5月21日 下午 02:26:36.630
Tid: 8   2021年5月21日 下午 02:26:36.630
Tid: 8   2021年5月21日 下午 02:26:36.631
Tid: 8   2021年5月21日 下午 02:26:36.632
Tid: 8   2021年5月21日 下午 02:26:36.632
Tid: 8   2021年5月21日 下午 02:26:36.633
Tid: 8   2021年5月21日 下午 02:26:36.634
Tid: 8   2021年5月21日 下午 02:26:36.634
Tid: 8   2021年5月21日 下午 02:26:36.635
Tid: 8   2021年5月21日 下午 02:26:36.636
Tid: 8   2021年5月21日 下午 02:26:36.636
Tid: 8   2021年5月21日 下午 02:26:36.637
Tid: 8   2021年5月21日 下午 02:26:36.638
Tid: 8   2021年5月21日 下午 02:26:36.638
Tid: 8   2021年5月21日 下午 02:26:36.639
Tid: 8   2021年5月21日 下午 02:26:36.641
Tid: 8   2021年5月21日 下午 02:26:36.641
Tid: 8   2021年5月21日 下午 02:26:36.642
Tid: 8   2021年5月21日 下午 02:26:36.643
Tid: 8   2021年5月21日 下午 02:26:36.644
Tid: 8   2021年5月21日 下午 02:26:36.644
Tid: 8   2021年5月21日 下午 02:26:36.645
Tid: 8   2021年5月21日 下午 02:26:36.646
Tid: 8   2021年5月21日 下午 02:26:36.647
Tid: 8   2021年5月21日 下午 02:26:36.647
Tid: 8   2021年5月21日 下午 02:26:36.648
Tid: 8   2021年5月21日 下午 02:26:36.649
Tid: 8   2021年5月21日 下午 02:26:36.650
Tid: 8   2021年5月21日 下午 02:26:36.650
Tid: 8   2021年5月21日 下午 02:26:36.651
Tid: 8   2021年5月21日 下午 02:26:36.652
Tid: 8   2021年5月21日 下午 02:26:36.652
Tid: 8   2021年5月21日 下午 02:26:36.652
Tid: 8   2021年5月21日 下午 02:26:36.653
Tid: 8   2021年5月21日 下午 02:26:36.654
Tid: 8   2021年5月21日 下午 02:26:36.655
Tid: 8   2021年5月21日 下午 02:26:36.656
Tid: 8   2021年5月21日 下午 02:26:36.658
Tid: 8   2021年5月21日 下午 02:26:36.658
Tid: 8   2021年5月21日 下午 02:26:36.659
Tid: 8   2021年5月21日 下午 02:26:36.660
Tid: 8   2021年5月21日 下午 02:26:36.660
Tid: 8   2021年5月21日 下午 02:26:36.661
Tid: 8   2021年5月21日 下午 02:26:36.662
Tid: 8   2021年5月21日 下午 02:26:36.662
Tid: 8   2021年5月21日 下午 02:26:36.663
Tid: 8   2021年5月21日 下午 02:26:36.664
Tid: 8   2021年5月21日 下午 02:26:36.664
Tid: 8   2021年5月21日 下午 02:26:36.665
Tid: 8   2021年5月21日 下午 02:26:36.666
Tid: 8   2021年5月21日 下午 02:26:36.666
Tid: 8   2021年5月21日 下午 02:26:36.667
Tid: 8   2021年5月21日 下午 02:26:36.668
Tid: 8   2021年5月21日 下午 02:26:36.669
Tid: 8   2021年5月21日 下午 02:26:36.669
Tid: 8   2021年5月21日 下午 02:26:36.670
Tid: 8   2021年5月21日 下午 02:26:36.671
Tid: 8   2021年5月21日 下午 02:26:36.672
Tid: 8   2021年5月21日 下午 02:26:36.673
Tid: 8   2021年5月21日 下午 02:26:36.675
Tid: 8   2021年5月21日 下午 02:26:36.675
Tid: 8   2021年5月21日 下午 02:26:36.676
Tid: 8   2021年5月21日 下午 02:26:36.677
Tid: 14  2021年5月21日 下午 02:26:36.678
Tid: 15  2021年5月21日 下午 02:26:36.679
Tid: 16  2021年5月21日 下午 02:26:36.680
Tid: 17  2021年5月21日 下午 02:26:36.681
Tid: 18  2021年5月21日 下午 02:26:36.681
Tid: 20  2021年5月21日 下午 02:26:36.683
Tid: 9   2021年5月21日 下午 02:26:36.683
Tid: 19  2021年5月21日 下午 02:26:36.684
Tid: 10  2021年5月21日 下午 02:26:36.685
Tid: 11  2021年5月21日 下午 02:26:36.685
Tid: 12  2021年5月21日 下午 02:26:36.687
Tid: 13  2021年5月21日 下午 02:26:36.688

到此这篇关于C#使用读写锁解决多线程并发问题的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

本文标题: C#使用读写锁解决多线程并发问题

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

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

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C#使用读写锁解决多线程并发问题
    一、简介 在开发程序的过程中,难免少不了写入错误日志这个关键功能。实现这个功能,可以选择使用第三方日志插件,也可以选择使用数据库,还可以自己写个简单的方法把错误信息记录到日志文件。现...
    99+
    2024-04-02
  • C语言多线程开发中死锁与读写锁问题怎么解决
    今天小编给大家分享一下C语言多线程开发中死锁与读写锁问题怎么解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。死锁有时,一个...
    99+
    2023-06-30
  • C语言多线程开发中死锁与读写锁问题详解
    目录死锁读写锁死锁 有时,一个线程需要同时访问两个或更多不同的共享资源,而每个资源又都由不同的互斥量管理。当超过一个线程加锁同一组互斥量时,就有可能发生死锁; 两个或两个以上的进程在...
    99+
    2024-04-02
  • C#多线程系列之读写锁
    本篇的内容主要是介绍 ReaderWriterLockSlim 类,来实现多线程下的读写分离。 ReaderWriterLockSlim ReaderWriterLock 类:定义支...
    99+
    2024-04-02
  • java多线程并发问题如何解决
    在Java中,可以使用以下方法来解决多线程并发问题:1. 使用synchronized关键字:可以通过在方法或代码块前加上synch...
    99+
    2023-09-27
    java
  • C++多线程编程中的并发问题解析
    C++多线程编程中的并发问题解析随着计算机硬件的不断发展,多核处理器已经成为了主流。在这种情况下,使用多线程来充分利用多核处理器的性能,成为了程序开发中的一项重要技术。然而,在多线程编程中,由于多个线程之间的并发操作,常常会导致一些问题,这...
    99+
    2023-10-22
    解析 并发问题 C++多线程编程
  • springboot多线程高并发问题怎么解决
    要解决Spring Boot多线程高并发问题,你可以考虑以下几个方面:1. 线程池配置:合理配置线程池的大小、队列容量等参数,以满足...
    99+
    2023-08-24
    springboot
  • 使用redis分布式锁解决并发线程资源共享问题
    前言 众所周知, 在多线程中,因为共享全局变量,会导致资源修改结果不一致,所以需要加锁来解决这个问题,保证同一时间只有一个线程对资源进行操作 但是在分布式架构中,我们的服务可能会有n个实例,但线程锁只对同一...
    99+
    2024-04-02
  • 解决 PHP 多线程函数中的并发问题
    php 多线程函数中的并发问题可以通过使用以下方法解决:使用同步工具(如互斥锁)来管理多线程访问共享资源。使用支持互斥选项的函数,以确保函数在另一个线程执行时不会被重新调用。将非可重入函...
    99+
    2024-05-01
    php 并发
  • Java多线程读写锁ReentrantReadWriteLock类详解
    目录ReentrantReadWriteLock读读共享写写互斥读写互斥源码分析写锁的获取与释放读锁的获取与释放参考文献真实的多线程业务开发中,最常用到的逻辑就是数据的读写,Reen...
    99+
    2024-04-02
  • 如何解决Java多线程死锁问题
    死锁问题 死锁定义 多线程编程中,因为抢占资源造成了线程无限等待的情况,此情况称为死锁。 死锁举例 注意:线程和锁的关系是:一个线程可以拥有多把锁,一个锁只能被一个线程拥有。 当两个...
    99+
    2024-04-02
  • Java多线程死锁问题怎么解决
    解决Java多线程死锁问题的常用方法有以下几种:1. 避免使用多个锁:尽量减少使用多个锁来降低出现死锁的概率。2. 按照固定的顺序获...
    99+
    2023-09-22
    Java
  • C++并发编程:如何识别和解决死锁问题?
    在 c++++ 并发编程中,死锁问题发生在一或多个线程无限期等待其他线程释放资源时,导致程序挂起。我们可以使用 std::lock_guard 和 std::unique_lock 实现...
    99+
    2024-05-04
    c++ 死锁问题
  • 如何解决 C++ 多线程编程中常见的死锁问题?
    如何解决 c++++ 多线程编程中的常见死锁问题?避免死锁的技术:加锁顺序:始终以相同的顺序获取锁。死锁检测:使用算法检测并解决死锁。超时:为锁设置超时值,防止线程无限期等待。优先级反转...
    99+
    2024-05-13
    多线程编程 死锁 c++
  • C#在复杂多线程环境下使用读写锁同步写入文件
    代码一: class Program { static int LogCount = 1000; static int SumLogCount...
    99+
    2024-04-02
  • 如何解决Go语言中的并发文件的读写锁冲突问题?
    如何解决Go语言中的并发文件的读写锁冲突问题?在Go语言中,我们经常会遇到需要同时对一个文件进行读写操作的场景,比如并发地写日志文件。如果不加以控制,多个goroutine同时对同一个文件进行读写操作,就会产生冲突,导致数据丢失或不一致。为...
    99+
    2023-10-22
    并发 关键词:Go语言 文件读写锁冲突
  • Python互斥锁怎么解决多线程问题
    这篇文章给大家分享的是有关Python互斥锁怎么解决多线程问题的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。python主要应用领域有哪些1、云计算,典型应用OpenStack。2、WEB前端开发,众多大型网站均...
    99+
    2023-06-14
  • C++ 函数如何解决并发编程中的死锁问题?
    在 c++++ 中,使用互斥量函数可以解决多线程并发编程中的死锁问题。具体步骤如下:创建一个互斥量;当线程需要访问共享变量时,获得互斥量;修改共享变量;释放互斥量。这样可以确保任何时刻只...
    99+
    2024-04-26
    并发编程 死锁 c++
  • Linux多线程及多线程并发访问同一块内存的问题怎么解决
    这篇文章主要介绍了Linux多线程及多线程并发访问同一块内存的问题怎么解决的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Linux多线程及多线程并发访问同一块内存的问题怎么解决文章都会有所收获,下面我们一起来看...
    99+
    2023-07-05
  • 如何解析Java多线程读写锁ReentrantReadWriteLock类
    这篇文章将为大家详细讲解有关如何解析Java多线程读写锁ReentrantReadWriteLock类,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。真实的多线程业务开发中,最常用到的逻辑就是...
    99+
    2023-06-22
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作