[英]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.