.Net 并发写入文件的多种方式
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 = ;//请求写入日志次数
var successCount =;//执行成功次数
var failCount = ;//执行失败次数
//模拟100000次用户请求写入日志操作
Parallel.For(, 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 = ;//请求写入日志次数
var successCount =;//执行成功次数
var failCount = ;//执行失败次数 var stopWatch = Stopwatch.StartNew();
//模拟100000次用户请求写入日志操作
var result=Parallel.For(, 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 = ;//请求写入日志次数
var successCount = ;//执行成功次数
var failCount = ;//执行失败次数 var stopWatch = Stopwatch.StartNew();
//模拟100000次用户请求写入日志操作
var result = Parallel.For(, 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利用率.暂时还没找到多线程写入一个文件,不需要加读锁的方法,如果有,请告知.
.Net 并发写入文件的多种方式的更多相关文章
- 用读写锁三句代码解决多线程并发写入文件 z
C#使用读写锁三句代码简单解决多线程并发写入文件时提示“文件正在由另一进程使用,因此该进程无法访问此文件”的问题 在开发程序的过程中,难免少不了写入错误日志这个关键功能.实现这个功能,可以选择使用第三 ...
- Linux中删除特殊名称文件的多种方式
今日分享:我们在肉体的疾病方面花了不少钱,精神的病害方面却没有花什么,现在已经到了时候,我们应该有不平凡的学校.--<瓦尔登湖> 前言 我们都知道,在linux删除一个文件可以使用rm命令 ...
- C#使用读写锁三行代码简单解决多线程并发写入文件时线程同步的问题
(补充:初始化FileStream时使用包含文件共享属性(System.IO.FileShare)的构造函数比使用自定义线程锁更为安全和高效,更多内容可点击参阅) 在开发程序的过程中,难免少不了写入错 ...
- C#使用读写锁解决多线程并发写入文件时线程同步的问题
读写锁是以 ReaderWriterLockSlim 对象作为锁管理资源的,不同的 ReaderWriterLockSlim 对象中锁定同一个文件也会被视为不同的锁进行管理,这种差异可能会再次导致文件 ...
- ansible下载文件的多种方式
对于ansible来说,下载文件是一个很重要的课题,这是build或者deploy的第一步,通常来讲由于不同项目的差异,可能我们的代码包或者资源文件保存在于http,github,nexus,ftp, ...
- PHP导出excel文件的多种方式
1.第一种实现的方法 set_time_limit(0); //逐条导出数据 ob_end_clean(); header("Content-type: application/vnd.ms ...
- 11. python读写文件的多种方式
一.txt文件 with open('users.txt','r') as user_file: data = user_file.readlines() users = [] for line in ...
- Java 读取TXT文件的多种方式
1).按行读取TXT文件package zc;import java.io.BufferedReader;import java.io.File;import java.io.FileNotFound ...
- SQL映射文件实现多种方式查询
1.单条件查询在test中代码如下 2.多条件查询时需要把查询条件编辑为对象或者是集合传入,例如 通过对象进行查询 或者是通过集合进行查询列如Map集合 还有就是通过@Param注解实现多参数的入参, ...
随机推荐
- redis多实例
1.首先在发布系统: 2.安装多实例利用cmd命令安装,切换目录到redis下 (1)首先拷贝一个redis的conf文件(如redis_6380.conf),并且修改里面的服务端口号.日志端口号,以 ...
- python基础入门之对文件的操作
**python**文件的操作1.打开文件 打开文件:open(file,mode='r') file:操作文件的路径加文件名 #绝对路径:从根目录开始的 #相对路径:从某个路径开始 mode:操作文 ...
- 利用canvas实现刮刮乐效果
最近做了个情人节表白的项目,表白内容时被遮盖的,刮开后才能显示,并且刮开一定比例后清空所有遮罩. function guaguale(obj,w,h){//obj时canvas元素 var canva ...
- 利用websocket实现微信二维码码扫码支付
由于业务需要引入微信扫码支付,故利用websocket来实现消息推送技术. 实现大致流程:首先客户端点击微信支付按钮,触发微信支付接口,同时微信支付响应成功参数后,连接websocket客户端,此刻利 ...
- macOS 安装 ctags
macOS 安装 ctags macOS 自带一个 ctags,但是不支持 -R 参数,递归产生tags文件 $ ctags -R --exclude=.git --exclude=log * cta ...
- Exp3 免杀原理与实践 20154320 李超
基础知识 如何检测出恶意代码 基于特征码的检测:分析指令的统计特性.代码的结构特性等.如果一个可执行文件(或其他运行的库.脚本等)拥有一般恶意代码所通有的特征(开启后门等)则被认为是恶意代码 启发式恶 ...
- some knowledge of the IT world
IT世界一切皆是可信息化(数据的转换)即信息记录一切,对信息的控制{存储,运算,传输{信息的位置转移},转换}就是对一切的控制{硬件(实质维)以信息的控制{软件形式(存在维)}进行操作} 信息本身的实 ...
- Leetcode 34 Find First and Last Position of Element in Sorted Array 解题思路 (python)
本人编程小白,如果有写的不对.或者能更完善的地方请个位批评指正! 这个是leetcode的第34题,这道题的tag是数组,需要用到二分搜索法来解答 34. Find First and Last Po ...
- 制作系统U盘
没有任何宣传软件成分昂,我就是这做的. 1.在百度搜索上搜索“通用PE大师”,点开了这个网站http://up.6615261.cn/index.html,打开之后如下图,下载这个二合一版本 2.在电 ...
- 推导正交投影(Orthographic Projection)
定义六个面 left right bottom top near far 然后三个轴分开考虑 x轴 视椎体的x范围在[l,r],我们要变换到[-1,1] 1 减去l变换到[0, r-l] 2 乘以 ...