使用异步/等待 - C# [关闭]

Posted

技术标签:

【中文标题】使用异步/等待 - C# [关闭]【英文标题】:using async/await - c# [closed] 【发布时间】:2021-12-20 20:40:29 【问题描述】:

我有以下代码。代码究竟做了什么?为什么我们需要StopwatchXmlSerializerMemoryStream。使用async/await 是否有更简单的方法? 使用contentResponse.Result; 也会导致死锁情况。

    public T GetResponse<T>()
    
        var response = default(T);

            using (HttpClient httpClient = new HttpClient())
            
                httpClient.DefaultRequestHeaders.Add("Authorization", authHeader);

                Stopwatch watch = Stopwatch.StartNew();

                var contentResponse = httpClient.GetAsync(uri.ToString());

                try
                
                    contentResponse.Wait();
                
                catch (Exception exception)
                

                

                var wsResponseContent = contentResponse.Result;
                wsResponseContent.Content.LoadIntoBufferAsync();
                var resultTask = wsResponseContent.Content.ReadAsStringAsync();
                resultTask.Wait();

                string wsResponseContentText = resultTask.Result;

                if (watch.IsRunning)
                    watch.Stop();

                wsResponseContent.EnsureSuccessStatusCode();
                if (wsResponseContent.IsSuccessStatusCode)
                
                    XmlSerializer result = new XmlSerializer(typeof(T));

                    using (MemoryStream sresultStream = new MemoryStream(Encoding.UTF8.GetBytes(wsResponseContentText)))
                    
                        response = (T)result.Deserialize(sresultStream);
                        sresultStream.Close();
                    
                
            
       
        return response;
    

【问题讨论】:

这段代码在很多方面都很糟糕,需要很长时间才能回答。从阅读开始:***.com/a/69859015/2141621 至于秒表——我看不到它在任何地方都被使用过,所以它几乎和它不存在一样——除了它增加了复杂性。 好吧,秒表确实被创建、启动和停止了。所有有用的操作,如果你最终要读取它的值(不像这里,它被忽略了)。摆脱它 @JonasH 绝对不可能,“代码到底是做什么的?”代码审查不解释代码。 通常秒表有两种使用方式,要么对操作进行基准测试(“这需要多长时间” - 在这种情况下,它会在升级到生产之前被删除)或记录(同样需要多长时间这样做了)。在这种情况下,watch 的范围在 using 块内。它被创建、启动和停止,但从不读取。它与并行处理没有有关。这里的唯一目的是测量StartNewStop 之间经过的时间 【参考方案1】:

此代码中不需要秒表。通常用作诊断工具,它可能很有用。但在这种情况下不是。 从下面的代码看来,它读取了通过网络发送的字符串并尝试将其读取为 XML 格式。因此需要MemoryStreamXMLSerializer

至于你的僵局情况。这总是因为两个实例试图访问同一个实例。您可以通过正确地等待您的功能来避免这种情况。 如果oyu真的想要一个合适的async,实现如下功能:

public async Task<T> GetResponse<T>()

    var response = default(T);

    using (HttpClient httpClient = new HttpClient())
    
        httpClient.DefaultRequestHeaders.Add("Authorization", authHeader);

        var contentResponse = await httpClient.GetAsync(uri.ToString());

        await wsResponseContent.Content.LoadIntoBufferAsync();
        var wsResponseContentText = await wsResponseContent.Content.ReadAsStringAsync();

        wsResponseContent.EnsureSuccessStatusCode();
        
        if (wsResponseContent.IsSuccessStatusCode)
        
            XmlSerializer result = new XmlSerializer(typeof(T));

            using (MemoryStream sresultStream = new MemoryStream(Encoding.UTF8.GetBytes(wsResponseContentText)))
            
                response = (T)result.Deserialize(sresultStream);
                sresultStream.Close();
             
        
    
   
    return response;

在上面的代码中,如果一个函数中有async。我们可以等待结果而不是任务。比如下面的代码:

public async Task<int> GetNumber5Async() 
    return 5;

如果我们像你以前那样称呼它:

var number5 = GetNumber5Async(); // Returns Task<int>

我们将收到一个我们需要从中获取结果的任务。但是,如果我们立即等待任务:

var number5 = await GetNumber5Async(); // Returns 5

我们会立即返回结果,无需执行number5.Wait(); number5.result;


乌龟一路向下

使用异步方法时您需要了解的一件事是“Turtes all way Down”的概念。 简单地说,如果您在任何地方都有异步方法,那么您的所有方法都会调用它,并且调用这些方法也需要是异步的。 那么异步方法需要什么?

    In 需要是Task。因此,它需要返回一个封装了值的Task,而不是返回一个值。

例如:

// None Async
public int GetNumber5()  return 5; 

// Async
public async Task<int> GetNumber5Async()  return 5; 
    需要async 描述符才能使用和调用await 函数。

例如:

// None Async
void GetANumber()
    var num5 = GetNumber5();


// Async
async void GetANumber()
    var num5 = await GetNumber5Async();


使用异步避免死锁

通常等待一个函数就足够了。但如果它仍然会产生死锁,您可以添加 '.ConfigureAwait(false);'

例如:

var num5 = await GetNumber5Async().ConfigureAwait(false);

【讨论】:

啊,别人用“Turtles all way Down”来描述async/await编码 Stephen Cleary 的粗略规则:如果您正在编写库代码,请使用ConfigureAwait(false),如果您不是,请不要到目前为止对我有用

以上是关于使用异步/等待 - C# [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

使用异步等待 C# Xamarin

C# 异步命名管道永远等待

Swift 中的 C# 异步等待

如何在c#中正确实现等待异步[重复]

C# 理解阻塞 UI 和异步/等待与 Task.Run 的问题?

在 C# 中异步等待继续后的实时 AutoResetEvent