不能期待的是“射击与遗忘”-使用安全吗?

时间:2019-12-26 07:58:07

标签: c# async-await

我们需要在代码中写一些日志。
 日志是通过网络请求编写的,该请求记录了一些信息(记录器是Http-API模块),但是,如果API中存在异常,我们不想破坏整个流程。

我不使用Task.Run进行这种操作,因为它很糟糕。

但是后来我想到了non-awaitable-inline-function,像这样:

private static readonly HttpClient client = new HttpClient();

void Main()
{

    Console.WriteLine(1);
    async Task LogMe()
    {
        var response = await client.PostAsync("http://www.NOTEXISTS.com/recepticle.aspx", null);
        var responseString = await response.Content.ReadAsStringAsync();
        //response.EnsureSuccessStatusCode(); // I don't need this here
        Console.WriteLine("error");
    }

    //some code
    LogMe();
    //some code
    Console.WriteLine(3);

}

输出:

1
2

问题:

以“一劳永逸”模式登录是否可行?
如果我在此内联方法中创建对象该怎么办?

2 个答案:

答案 0 :(得分:5)

  

我不使用Task.Run进行这种操作,因为它很糟糕。

重要的是要识别/指定此类陈述的上下文。 Task.Run在ASP.NET上是不正确的。在客户端的GUI应用程序中使用完全可以。

  

我们需要在代码中写一些日志。

我强烈建议使用已建立的日志库。它们中的大多数通过使用内存队列来工作,该队列由代码(同步)写入,并由后台线程连续处理。这是一个公认的模式。

  

以“一劳永逸”模式登录是否可行?

“一劳永逸”的问题之一是您不知道何时有错误。建立的日志库都具有某种系统来处理与日志后端通信的错误。您发布的代码只会忽略它们。

  

如果我在此内联方法中创建对象怎么办,何时将它们进行GC处理?

从概念上讲,只要异步方法将来会继续执行,它们就充当GC的“根”。因此,方法完成后,将对本地对象进行GC处理。

答案 1 :(得分:4)

  

我不使用Task.Run进行这种操作,因为它很糟糕。

这还不错。这只是工具带中的另一种工具。

理解异步方法如何工作很重要。异步方法都像其他方法一样开始同步运行。魔术发生在作用于不完整await的第一个Task上。此时,await看到不完整的Task,并且方法 returns 。通常,它返回自己不完整的Task,除非该方法为async void,则它什么也不返回。

异步与方法运行无关。关于方法等待

因此,在您的代码中,LogMe()将开始在同一线程上运行。只有在等待响应时,Task才会返回到调用堆栈中,您可以随时等待。

设置请求和发送请求所花费的时间并不多。因此,您不会注意到它。但是,如果您做的事情在请求之前确实做了一些CPU密集型工作,那么使用Task.Run是适当的,因为这会告诉它在不同的线程上 start ,因此它不会阻止你。

如果这是ASP.NET(不是Core),那么您可能会受益于使用Task.Run或在.ConfigureAwait(false)上使用PostAsync,因为否则它将尝试返回到相同的同步上下文(当前传入HTTP请求的上下文),这意味着当前请求无法在LogMe()完成之前完成。

但是,如果您要开火而忘记了,那么请记住,无论启动什么操作都不会知道操作是否成功。如果要在其他地方(文本文件或其他内容)记录故障,则应在try内使用catch / LogMe。如果发送请求时出现问题(DNS查找失败,无响应等),PostAsync将引发异常。