简体   繁体   English

如何在此代码中正确使用 async 和 await?

[英]How do I properly use async and await in this code?

My Windows Form application freezes when deserializing "all episodes" from a local XML-file.从本地 XML 文件反序列化“所有剧集”时,我的 Windows 窗体应用程序冻结。 I want to solve this problem by implementing asynchronous programming but unfortunately I do not succeed with the implementation (I am a beginner and it's the first time I use await and async).我想通过实现异步编程来解决这个问题,但不幸的是我没有成功实现(我是初学者,这是我第一次使用 await 和 async)。

Working (but freezing) synchronous execution:工作(但冻结)同步执行:

public class EpisodeRepository : IEpisodeRepository<Episode>
{
    SerializerForXml dataManager;
    List<Episode> listOfEpisodes;
    
    public EpisodeRepository()
    {
        dataManager = new SerializerForXml();
        listOfEpisodes = new List<Episode>();
        listOfEpisodes = GetAll();
    }
    public List<Episode> GetAll()
    {
        List<Episode> listOfEpisodesDeserialized = new List<Episode>();
        try
        {
            listOfEpisodesDeserialized = dataManager.DeserializeEpisode());
        }
        catch (Exception)
        {
            throw new SerializerException("Episode.xml", "Error when deserialize");
        }

        return listOfEpisodesDeserialized;
    } 
}
public interface IEpisodeRepository<T> : IRepository<T> where T : class
{
    List<T> GetAllFeed(string url);
}

public interface IRepository<T> where T : class
{
    void Create(T entity);
    void Delete(int index);
    void Update(int index, T entity);
    void SaveChanges();
    List<T> GetAll();
    int GetIndex(string name);
}

My attempt to solve it with async and await:我尝试用 async 和 await 解决它:

I changed the interface method in IRepository to Task<List<T>> GetAll();我将 IRepository 中的接口方法改为Task<List<T>> GetAll(); and then I changed the method to:然后我将方法更改为:

public async Task<List<Episode>> GetAll()
        {
            List<Episode> listOfEpisodesDeserialized = new List<Episode>();
            try
            {
                listOfEpisodesDeserialized  = await Task.Run(() => dataManager.DeserializeEpisode());
            }
            catch (Exception)
            {
                throw new SerializerException("Episode.xml", "Error when deserialize");
            }

            return listOfEpisodesDeserialized;
        } 

This code won't compile because the method returns此代码将无法编译,因为该方法返回

"System.Threading.Tasks.Task<System.Collections.Generic.List<Models.Episode>>" when the field is of type "System.Collections.Generic.List<Models.Episode>". “System.Threading.Tasks.Task<System.Collections.Generic.List<Models.Episode>>”,当该字段的类型为“System.Collections.Generic.List<Models.Episode>”。

This code also causes some errors to other methods using GetAll().FirstOrDefault(..) .此代码还会导致使用GetAll().FirstOrDefault(..)其他方法出现一些错误。

You should fix a few things to make the code work.您应该修复一些问题以使代码正常工作。

First, in your repository interface change the return type of the GetAll() function like this:首先,在您的存储库界面中,像这样更改GetAll()函数的返回类型:

public interface IRepository<T> where T : class
{
    void Create(T entity);
    void Delete(int index);
    void Update(int index, T entity);
    void SaveChanges();
    Task<List<T>> GetAll(); // <= Wrapped in a "Task"
    int GetIndex(string name);
}

Then find all the places where that function was called, and put an await before the call.然后找到调用该函数的所有位置,并在调用之前放置一个await Also don't forget to make those caller functions async as well, and their calls await.也不要忘记使这些调用者函数也异步,并且它们的调用等待。

Quick solution with Lazy使用Lazy快速解决

  public EpisodeRepository()
    {
        dataManager = new SerializerForXml();
        listOfEpisodesLazy = new Lazy<List<Episode>>(() => GetAll());
    }

  private listOfEpisodes => listOfEpisodesLazy.Value;

Solution with deporting the heavy processing in a Task.驱逐任务中繁重处理的解决方案。 But you must add a check before using listOfEpisodes variable.但是您必须在使用listOfEpisodes变量之前添加检查。

  public EpisodeRepository()
    {
        dataManager = new SerializerForXml();
        listOfEpisodes = null;
        PopulateListOfEpisodes();
    }

  private async Task PopulateListOfEpisodes()
  {
    return await Task.Run(() => {
        listOfEpisodes  = GetAll();
    };
  }

In any case, heavy operations should be avoided in the constructor.在任何情况下,都应该避免在构造函数中进行繁重的操作。

Due to CPU-bound nature of your code I'd suggest wrapping it in Task.Run directly in WinForms app.由于您的代码受 CPU 限制的性质,我建议直接在 WinForms 应用程序Task.Run其包装在Task.Run中。

public async void Button_Click(object sender, MouseEventArgs agrs)
{
    try
    {
         List<Episode> data = await Task.Run(() => repository.GetAll());
         // Update UI
    }
    catch (Exception e)
    { 
        // Handle exception
    }
}

There's an excellent article which explains why.有一篇很好的文章解释了原因。

And the reason why I wrapped everything in a try-catch is an async void method which tend to be error-prone and basically will swallow your exception without populating it back to the UI thread.原因就是我在一个try-catch包裹一切都是async void这往往是容易出错,基本上会吞噬你的异常没有填充它回到UI线程方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM