我很擅长使用C#进行线程化。我有一个Searchfield,它会在按下的每个Key上触发针对API的HTTP-Request。在请求之后,Searchresult显示在我的SearchTab中的ListView中。当然,这会冻结UI几毫秒,直到HTTP-Request完成。所以我试着让Request Async。
private async void textBoxSearch_KeyUp(object sender, KeyEventArgs e)
{
SearchTab searchTab;
Task<SearchContainer<SearchMovie>> searchTask = searchMovie(textBoxSearch.Text);
if (searchTabExists())
{
searchTab = getSearchTab();
}
else
{
searchTab = new SearchTab();
mainTabControl.Items.Insert(mainTabControl.Items.Count, searchTab);
}
searchTab.IsSelected = true;
SearchContainer<SearchMovie> results = await searchTask;
searchTab.updateSearch(results);
}
async Task<SearchContainer<SearchMovie>> searchMovie(String query)
{
var today = await Task.FromResult<SearchContainer<SearchMovie>>(tmdbClient.SearchMovie(query, "de"));
return today;
}
此代码使它更好一点,但它仍然冻结了UI,因为在某些时候它必须等待API调用。我希望能够不间断地平滑地输入我的搜索,如果API-Call在另一次搜索之前及时完成(KeyPressed),则显示结果。
你如何在C#中解决这样的问题?在JAVA中,我会使用AsyncWorker在搜索完成后调用Displaymethod(或者如果进行另一次搜索则取消worker),这样就不会在Main-Thread中进行搜索和显示。
C#中是否有类似的构造?如果我搜索多线程,我只能找到异步等待解决方案。或者可以按照我希望的方式使用它。
答案 0 :(得分:5)
Task.FromResult
根据结果创建一个已经成功的任务。在您的示例中,结果来自对tmdbClient.SearchMovie()
的调用。我认为这个电话是阻止的。
此代码并非真正异步,因为您仍在等待阻塞调用。您需要tmdbClient.SearchMovie()
方法的异步版本。通常,这些是显而易见的,因为它们以Async
为后缀。因此,您需要一个类似tmdbClient.SearchMovieAsync()
的方法。
如果客户端库没有提供此类方法,则需要将阻塞调用转换为异步调用。
这样做的一个简单但有缺陷且有问题的方法是使用Task.Run
:
(这是未经测试的伪代码)
Task<SearchContainer<SearchMovie>> searchMovie(String query)
{
return Task.Run(() => tmdbClient.SearchMovie(query, "de"));
}
这里的问题是你正在使用一个线程进行网络IO调用,这是对资源的错误使用:后台线程正在等待阻塞调用的结果,并且在阻塞调用之前不能用于任何事情完成。它应该为您提供响应更快的UI,但线程资源成本与线程相同,但异步的复杂性。