1、简介

本文主要演示日常开发中利用多线程写入文件存在的问题,以及解决方案,本文使用最常用的日志案例!

2、使用File.AppendAllText写入日志

这是种常规的做法,通过File定位到日志文件所在位置,然后写入相应的日志内容,代码如下:

        static string _filePath = @"C:\Users\zhengchao\Desktop\测试文件.txt";static void Main(string[] args){WriteLogAsync();Console.ReadKey();}static void WriteLogAsync(){var logRequestNum = 100000;//请求写入日志次数var successCount =0;//执行成功次数var failCount = 0;//执行失败次数//模拟100000次用户请求写入日志操作Parallel.For(0, logRequestNum, i =>{try{var now = DateTime.Now;var logContent = $"当前线程Id:{Thread.CurrentThread.ManagedThreadId},日志内容:暂时没有,日志级别:Warn,写入时间:{now.ToString()}";File.AppendAllText(_filePath, logContent);successCount++;}catch (Exception ex){failCount++;Console.WriteLine(ex.Message);}});Console.WriteLine($"Request Count:{logRequestNum}. Success Count:{successCount} Failed Count:{failCount}.");}

报错了,原因,Windows不允许多个线程同时操作同一个文件,所以,抛异常.所以必须解决这个问题。

3、利用ReadWriterSlim解决多线程征用文件问题

关于ReadWriterSlim的使用,在本人的这篇随笔中已介绍,在其基础上,对SynchronizedCache类稍稍改造,形成一个SynchronizedFile类,对相关操作代码进行线程安全处理,即能解决当前的问题,代码如下:

   public class SynchronizedFile{private static ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();/// <summary>/// 线程安全的写入文件操作/// </summary>/// <param name="action"></param>public static void WriteFile(Action action){cacheLock.EnterWriteLock();try{action.Invoke();}finally{cacheLock.ExitWriteLock();}}}

调用代码如下所示:

        static string _filePath = @"C:\Users\zhengchao\Desktop\测试文件.txt";static void Main(string[] args){WriteLogSync();Console.ReadKey();}/// <summary>/// 多线程同步写入文件/// </summary>static void WriteLogSync(){var logRequestNum = 10000;//请求写入日志次数var successCount =0;//执行成功次数var failCount = 0;//执行失败次数var stopWatch = Stopwatch.StartNew();//模拟100000次用户请求写入日志操作var result=Parallel.For(0, logRequestNum, i =>{SynchronizedFile.WriteFile(() =>{try{var now = DateTime.Now;var logContent = $"当前线程Id:{Thread.CurrentThread.ManagedThreadId},日志内容:暂时没有,日志级别:Warn,写入时间:{now.ToString()}\r\n";File.AppendAllText(_filePath, logContent);successCount++;}catch (Exception ex){failCount++;Console.WriteLine(ex.Message);}});});if (result.IsCompleted){stopWatch.Stop();Console.WriteLine($"Request Count:{logRequestNum}. Success Count:{successCount} Failed Count:{failCount},总耗时:{stopWatch.ElapsedMilliseconds/1000}秒");}}

内容全部写入成功,但是还没有结束,原因是,反编译

一直反编译下去,会发现

用的是同步Api,所以代码可以继续优化,同步意味着每个线程在写入文件时,当前的写入托管代码会转换成托管代码,最后,Windows会把当前写入操作的数据初始化成IRP数据包传给硬件设备,之后硬件设备开始执行写入操作。这个过程,当前线程在和硬件交互时,不会返回到线程池,而是被Windows置为休眠状态,等待硬件设置执行写入操作完毕后,接着Windows会唤起该线程,最后又回到我的托管代码也就是C#代码中,继续执行下面的逻辑.所以当前的日志写入代码可以优化,使用异步Api来做.这样当前线程不会等待硬件设备,而是返回线程池.提高CPU的利用率.

4、优化代码

        static string _filePath = @"C:\Users\zhengchao\Desktop\测试文件.txt";static void Main(string[] args){WriteLogAsync();Console.ReadKey();}/// <summary>/// 多线程异步写入文件/// </summary>static void WriteLogAsync(){var logRequestNum = 10000;//请求写入日志次数var successCount = 0;//执行成功次数var failCount = 0;//执行失败次数var stopWatch = Stopwatch.StartNew();//模拟100000次用户请求写入日志操作var result = Parallel.For(0, logRequestNum, i =>{SynchronizedFile.WriteFile(() =>{try{var now = DateTime.Now;var logContent = $"当前线程Id:{Thread.CurrentThread.ManagedThreadId},日志内容:暂时没有,日志级别:Warn,写入时间:{now.ToString()}\r\n";var utf8NoBom = new UTF8Encoding(false, true);//去掉Dom头using (StreamWriter writer = new StreamWriter(_filePath, true, utf8NoBom)){writer.WriteAsync(logContent);}successCount++;}catch (Exception ex){failCount++;Console.WriteLine(ex.Message);}});});if (result.IsCompleted){stopWatch.Stop();Console.WriteLine($"Request Count:{logRequestNum}. Success Count:{successCount} Failed Count:{failCount},总耗时:{stopWatch.ElapsedMilliseconds / 1000}秒");}}

虽然效果差不多,但是能提升CPU利用率.暂时还没找到多线程写入一个文件,不需要加读锁的方法,如果有,请告知.

转载于:https://www.cnblogs.com/GreenLeaves/p/10617306.html

.Net 并发写入文件的多种方式相关推荐

  1. vue引用js文件的多种方式

    本文主要介绍了vue引用js文件的多种方式,本文大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下 1.vue-cli webpack全局引入jquery (1) 首先 npm inst ...

  2. vue引用js文件的多种方式(推荐)

    vue引用js文件的多种方式(推荐) Day_by_day93 这篇文章主要介绍了vue引用js文件的多种方式,本文大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下 1.vue-cli ...

  3. linux删除文件_Linux中删除特殊名称文件的多种方式

    今日分享:我们在肉体的疾病方面花了不少钱,精神的病害方面却没有花什么,现在已经到了时候,我们应该有不平凡的学校.--<瓦尔登湖> 前言 我们都知道,在linux删除一个文件可以使用rm命令 ...

  4. Java 读取TXT文件的多种方式-行读取,字节读取

    Java 读取TXT文件的多种方式 1).按行读取TXT文件 package zc; import java.io.BufferedReader; import java.io.File; impor ...

  5. C#使用读写锁三行代码简单解决多线程并发写入文件时线程同步的问题

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

  6. java读取csv文件的多种方式

    csv文件的介绍 以下是来自百度百科的介绍 逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和 ...

  7. HTML上传文件的多种方式

    1. 传统方式 <form id="upload-form" action="upload.php" method="post" en ...

  8. 关于模拟器拉取文件的多种方式

    一.文件助手拉取文件 第一步:在夜神模拟器中,找到文件管理器 第二步:勾上需要拉取到电脑中的文件 第三步:点击模拟器文件助手 第四步:点击模拟器左下角的按钮>>粘贴选择项,即可将文件复制过 ...

  9. java— 读取JSON文件的多种方式

    大部分内容参考自: https://blog.csdn.net/csdn_halon/article/details/120287992 在开发过程中有时会遇到需要读取本地.json文件的需求,通常会 ...

  10. [笔记]Go语言写文件几种方式性能对比

    Go语言中写文件有多种方式,这里进行如下几种方式的速度对比: 打开文件,写入内容,关闭文件.如此重复多次 打开文件,写入内容,defer 关闭文件.如此重复多次 打开文件,重复多次写入内容,defer ...

最新文章

  1. 基于 FPGA 的以太网回环测试verilog实现UDP协议
  2. 赠你一只金色的眼 - 富集分析和表达数据可视化
  3. mysql 判断是数据类型_mysql数据类型判断
  4. 组件与.NET互操作
  5. 帆软图表切换接口和图标轮播接口
  6. oracle安装失败 主机名_PeopleTool 8.58.04 安装
  7. 无法修改计算机时间权限,Win10无法修改时间怎么办?Win10修改系统时间没有权限的解决方法...
  8. Multisim里导入没有的元器件(以NPN型三极管2N9013为例)
  9. matlab特征值分解
  10. AUTOSAR 基础软件的内在安全
  11. 十大免费响应式Joomla主题
  12. Android8.0适配问题java.lang.IllegalStateException: Only fullscreen opaque activities can request orie…
  13. 高德地图广告投放的优势、效果!
  14. VR 、SR 轴矢冠三个切面的显示
  15. 推荐一款语音识别软件
  16. CH37X 文件管理芯片使用及移植指南
  17. linux vim 命令无效,Linux vim 命令 command not found vim 命令详解 vim 命令未找到 vim 命令安装 - CommandNotFound ⚡️ 坑否...
  18. HTML页面上传图片直接预览
  19. String的使用方法
  20. Mac OS X在终端中打开文件夹窗口

热门文章

  1. 【转】用Terracotta实现Master-Worker
  2. 粒子场优化(Particle Field Optimization,PFO)
  3. 【VBA编程实例】 如何导出百度云盘的目录
  4. 机器学习非平衡数据集概述
  5. 智能优化算法:非洲秃鹫优化算法-附代码
  6. 高斯滤波程序编写 opencv C++ CSU
  7. 从零基础入门Tensorflow2.0 ----六、29keras_generator读取 kaggle 10 monkeys数据
  8. ArcGIS学习总结(11)——创建点要素并计算对应经纬度
  9. 【Arcpy】Arcpy核心
  10. windows 64位PHP5.5配置xhprof