首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C#异步技术,用于写入日志文件,但首先等待以前的日志I/O完成

C#异步技术,用于写入日志文件,但首先等待以前的日志I/O完成
EN

Stack Overflow用户
提问于 2022-08-05 04:35:43
回答 1查看 184关注 0票数 1

有时,我需要写入日志文件,有时会出现一系列快速日志请求,但我不想等待I/O。但是,我想要等待的是在写入下一个日志条目之前,I/O完成(如流关闭)。因此,如果第一个日志I/O请求繁忙,则进一步的I/O请求将礼貌地排队等待轮到它们,而不是互相践踏。

我已经想出了一个主意,这有什么理由行不通吗?使用Framework4.7.2和4.8,asp.net MVC web应用程序。

我在其他地方定义了一个静态的Task,所以它对应用程序是全局的。

代码语言:javascript
复制
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());
}

这似乎很有效,简单的压力测试如下:

代码语言:javascript
复制
ErrorLog("test.txt", string.Join(" ", Enumerable.Range(i++, 1000)));

重复了很多次。变量I只是为了在日志中看到每个写的顺序。

优点是,我不需要重写所有请求就可以异步化,并将ErrorLog转换为真正的异步函数。这是理想的,但今天修改的代码太多了。

我担心的是最后一次写,虽然当AppDomain请求完成时,它似乎已经完成,但我不认为这是一种保证.我想知道我是否需要在每个传入的web请求的末尾执行一个t.Wait(),这些请求可能会写入日志.只是为了确保在结束请求之前最后一个日志条目是完整的.

EN

回答 1

Stack Overflow用户

发布于 2022-08-07 14:20:05

您的问题是,您没有等待写入的Task结果,这意味着可以在中间拆下AppDomain。

理想情况下,如果您只是等待编写,您可以这样做:

代码语言:javascript
复制
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和一个日志队列来编写。

一个非常粗糙和现成的实现应该是这样的:

代码语言:javascript
复制
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));
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73244716

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档