有时,我需要写入日志文件,有时会出现一系列快速日志请求,但我不想等待I/O。但是,我想要等待的是在写入下一个日志条目之前,I/O完成(如流关闭)。因此,如果第一个日志I/O请求繁忙,则进一步的I/O请求将礼貌地排队等待轮到它们,而不是互相践踏。
我已经想出了一个主意,这有什么理由行不通吗?使用Framework4.7.2和4.8,asp.net MVC web应用程序。
我在其他地方定义了一个静态的Task,所以它对应用程序是全局的。
public static void ErrorLog(string file, string error)
{
if (t != null)
t.Wait();
//using file system async - doesn't use thread pool
var f = new FileStream(Path.Combine(HttpRuntime.AppDomainAppPath, "logs", file), FileMode.Append, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true);
var sWriter = new StreamWriter(f);
t = sWriter.WriteLineAsync($"### {error}").ContinueWith(c => sWriter.Close());
}这似乎很有效,简单的压力测试如下:
ErrorLog("test.txt", string.Join(" ", Enumerable.Range(i++, 1000)));重复了很多次。变量I只是为了在日志中看到每个写的顺序。
优点是,我不需要重写所有请求就可以异步化,并将ErrorLog转换为真正的异步函数。这是理想的,但今天修改的代码太多了。
我担心的是最后一次写,虽然当AppDomain请求完成时,它似乎已经完成,但我不认为这是一种保证.我想知道我是否需要在每个传入的web请求的末尾执行一个t.Wait(),这些请求可能会写入日志.只是为了确保在结束请求之前最后一个日志条目是完整的.
发布于 2022-08-07 14:20:05
您的问题是,您没有等待写入的Task结果,这意味着可以在中间拆下AppDomain。
理想情况下,如果您只是等待编写,您可以这样做:
public static async Task ErrorLog(string file, string error)
{
//using file system async - doesn't use thread pool
using (var f = new FileStream(Path.Combine(HttpRuntime.AppDomainAppPath, "logs", file), FileMode.Append, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
using (var sWriter = new StreamWriter(f))
{
await sWriter.WriteLineAsync($"### {error}"):
}
}但是,这不允许您不等待就放弃日志写入。相反,您需要实现一个BackgroundService和一个日志队列来编写。
一个非常粗糙和现成的实现应该是这样的:
public class LoggingService : BackgroundService
{
private Channel<(string file, string error)> _channel = new Channel.CreateUnbounded<(string, string)>();
protected override async Task ExecuteAsync(CancellationToken token)
{
while(true)
{
try
{
var (file, error) = await _channel.Reader.ReadAsync(token);
await WriteLog(file, error, token);
}
catch (OperationCanceledException)
{
break;
}
}
}
private async Task WriteLog(string file, string error, CancellationToken token)
{
using (var f = new FileStream(Path.Combine(HttpRuntime.AppDomainAppPath, "logs", file), FileMode.Append, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
using (var sWriter = new StreamWriter(f))
{
await sWriter.WriteLineAsync($"### {error}".AsMemory(), token):
}
}
public async Task QueueErrorLog(string file, string error)
{
await _channel.Writer.WriteAsync((file, error));
}
}https://stackoverflow.com/questions/73244716
复制相似问题