【问题标题】:What's wrong with this async/await method?这个 async/await 方法有什么问题?
【发布时间】:2019-02-04 02:11:53
【问题描述】:

我正在尝试将图像源属性设置为从 API 以 JSON 格式返回的 Uri。返回的 JSON 对象有两个属性。

我创建了以下帮助程序类来查询 API 并提取 Uri 属性:

public class GetPicUri
{
    public GetPicUri()
    {
    }

    private string _uri;

    public async Task<string> Get()
    {
        var uri = new Uri("https://randompicture.api/random");
        HttpClient getResponse = new HttpClient();
        var json = await getResponse.GetStringAsync(uri);

        var picture = JsonConvert.DeserializeObject<Picture>(json);

        _uri = picture.Uri;
        return _uri;
    }
}

这是用于反序列化 JSON 的模型:

public class Picture
{
    public Picture()
    {
    }

    public string Description { get; set; }
    public string Uri { get; set; }
}

问题是我无法访问 GetPicUri 类的字符串属性:

var picUri = new GetPicUri();
string imageSource = picUri.Get();

当我尝试这个时,我每次都会遇到异常:

无法从“System.Threading.Tasks.Task”转换为“字符串”

我尝试过以多种不同的方式对其进行切片,例如将 Uri 作为静态字符串,将 Get 方法设置为 async Task(或 void)并让它只更新静态字符串,并从构造函数中调用。我尝试将它作为属性的 Get 方法运行。但我无法让它工作。

所以我想我有两个问题,第一个是我在这里具体做错了什么,从设计的角度来看,正确的方法是什么?

【问题讨论】:

  • string imageSource = await picUri.Get(); 并创建包含方法async
  • 根本问题是你不明白什么是任务。一个字符串的任务是“我将来给你一个字符串”。所以你不能将字符串任务转换为字符串; “我以后给你一根弦”和“我是一根弦”是完全不同的两个东西。
  • 等待是异步等待。它的意思是“如果将来的字符串现在可用,就得到它;如果没有,找更多的工作做,在我们等待的时候做那个工作,当它可用时回到这里”。所以要把一个字符串的任务变成一个字符串,你等待它。
  • 好的,那么问题是我必须创建从异步调用它的方法。当我不能(或不想)这样做时,是否有正确的方法?在这种情况下,我调用的方法必须是异步的,因为 HttpClient 需要它。
  • 支持异步工作流是程序的属性,而不是方法的属性。使整个调用堆栈异步,直到事件处理程序。记住我说过的话:await 意味着去找更多的工作。整个程序必须合作,以确定在您异步等待时可以完成哪些工作。

标签: c# multithreading task


【解决方案1】:

如果方法实际上是异步的,最好在方法名称中添加Aync - 例如:

public async Task<string> GetAsync()
{
    var uri = new Uri("https://randompicture.api/random");
    HttpClient getResponse = new HttpClient();
    var json = await getResponse.GetStringAsync(uri);

    var picture = JsonConvert.DeserializeObject<Picture>(json);

    _uri = picture.Uri;
    return _uri;
}

调用方式:

await picUri.GetAsync();

【讨论】:

    【解决方案2】:

    您的 get 方法正在返回一个任务(基本上是一个对象包含状态等任务信息),任何异步方法都应返回任务(或 void),您需要使用 await 关键字异步等待此任务完成

    string imageSource = await picUri.Get();
    

    使用上述方法,你将不得不将调用方法也更改为 async ,你会发现 async 关键字扩散到你的代码中

    或者通过从 get 方法返回的任务对象中获取 Result 属性来强制方法同步运行(这基本上使得将 Get 方法声明为 async 没有用)

    string imageSource = picUri.Get().Result;
    

    【讨论】:

    • 注意:在async 方法上使用ResultWait 的情况很少。
    • Michael Randall 的评论可以加强。 除非您确定任务已完成,否则不要同步等待任务。如果你不遵循这个建议,你几乎肯定会产生一个可能死锁的程序。
    猜你喜欢
    • 2018-11-25
    • 2016-07-05
    • 2018-02-06
    • 2020-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多