我开始研究Task,async / await概念是c#,我很难理解它,至少我不知道如何实现它。我开始重写我之前编写的旧测试程序,但现在我没有使用线程,而是希望使用这些新概念。基本上布局如下:
我有一个简单的类,我下载网页的HTML内容。 我在另一个类中处理它,我基本上只是将页面解析为我的模型。稍后我想将它显示到我的UI。 问题是我的程序没有响应,它在我处理信息时阻止了用户界面。
我2天前开始学习这个,我在线阅读了很多东西,包括MSDN和一些博客,但我还是无法弄明白。也许有人可以提供一个外观
HtmlDOwnloadCOde:
public async Task<string> GetMangaDescriptionPage(string detailUrl)
{
WebClient client = new WebClient();
Stream data = await client.OpenReadTaskAsync(detailUrl);
StreamReader reader = new StreamReader(data);
string s = reader.ReadToEnd();
data.Dispose();
reader.Dispose();
data.Close();
reader.Close();
return s;
}
我的解析类代码:
public async Task<MangaDetailsModel> ParseMangaDescriptionPage()
{
ParseOneManga pom = new ParseOneManga();
string t1 = await pom.GetMangaDescriptionPage(selectedManga.url);
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(t1);
var divs = htmlDoc.DocumentNode.Descendants("div").Where(x => x.Attributes.Contains("id") &&
x.Attributes["id"].Value.Contains("title")).ToArray();
mangaDetails.mangaName = divs[0].Element("h1").InnerText;
mangaDetails.description = divs[0].Descendants("p").Single().InnerText ?? "DSA";
var tds = divs[0].Descendants("td");
int info = 0;
var chapters = htmlDoc.DocumentNode.Descendants("div").Where(x => x.Attributes.Contains("id") &&
x.Attributes["id"].Value.Contains("chapters")).ToArray();
var chapterUi = chapters[0].Descendants("ul").Where(x => x.Attributes.Contains("class") &&
x.Attributes["class"].Value.Contains("chlist"));
foreach (var li in chapterUi)
{
var liChapter = li.Descendants("li");
foreach (var h3tag in liChapter)
{
var chapterH3 = h3tag.Descendants("a").ToArray();
SingleManagFox chapterData = new SingleManagFox();
chapterData.name = chapterH3[1].InnerHtml;
chapterData.url = chapterH3[1].GetAttributeValue("href", "0");
mangaDetails.chapters.Add(chapterData);
}
};
return mangaDetails;
}
UI代码:
private async void mainBtn_Click(object sender, RoutedEventArgs e)
{
if (mangaList.SelectedItem != null)
{
test12((SingleManagFox)mangaList.SelectedItem);
}
}
private async void test12(SingleManagFox selectedManga)
{
selectedManga = (SingleManagFox)mangaList.SelectedItem;
MangaDetails mangaDetails = new MangaDetails(selectedManga);
MangaDetailsModel mdm = await mangaDetails.ParseMangaDescriptionPage();
txtMangaArtist.Text = mdm.artisName;
txtMangaAuthor.Text = mdm.authorName;
chapterList.ItemsSource = mdm.chapters;
}
对不起,如果它是微不足道的,但我无法弄清楚自己。
答案 0 :(得分:1)
当进行异步时,您需要尝试一直异步,并避免使用异步调用混合阻塞调用。
您在事件处理程序中使用async void
而没有await
。
尽管它是一个事件处理程序,但请尽量避免使用async void
。应更新test12
以返回Task
并在事件处理程序mainBtn_Click
中等待。
private async void mainBtn_Click(object sender, RoutedEventArgs e) {
if (mangaList.SelectedItem != null) {
await test12((SingleManagFox)mangaList.SelectedItem);
}
}
private async Task test12(SingleManagFox selectedManga) {
selectedManga = (SingleManagFox)mangaList.SelectedItem;
MangaDetails mangaDetails = new MangaDetails(selectedManga);
MangaDetailsModel mdm = await mangaDetails.ParseMangaDescriptionPage();
txtMangaArtist.Text = mdm.artisName;
txtMangaAuthor.Text = mdm.authorName;
chapterList.ItemsSource = mdm.chapters;
}
另请考虑更新网络电话以使用HttpClient
(如果有)。
class ParseOneManga {
public async Task<string> GetMangaDescriptionPageAsync(string detailUrl) {
using (var client = new HttpClient()) {
string s = await client.GetStringAsync(detailUrl);
return s;
}
}
}
参考: - Async/Await - Best Practices in Asynchronous Programming
答案 1 :(得分:0)
人们常常认为async-await意味着多个线程同时处理您的代码。除非您明确启动另一个线程,否则情况并非如此。
一个很好的比喻帮助我解释了async-await,这是this interview with Eric Lippert中使用的restauran隐喻。在中间某处搜索async-await。
Eric Lipperts将异步等待处理与一位不得不等待自己的水煮沸的厨师进行比较。他没有等待,而是环顾四周是否可以做其他事情。完成另一件事后,他回来看看水是否沸腾并开始处理沸水。
您的流程也是如此。只有一个线程忙(一次)。这个线程继续处理,直到他必须等待某事。这通常是一个相当长的过程,在不使用CPU核心的情况下处理,例如将文件写入磁盘,加载网页或从外部数据库查询信息。
你的线程一次只能做一件事。因此,当它忙于计算某些东西时,如果无法对操作员输入作出反应并且您的UI冻结,则直到计算完成。只有当你的线程有很多次等待其他进程完成时,异步等待才有用
如果调用异步函数,则可以确定该函数中的某个位置是等待的。事实上,如果你声明你的函数是异步的,并且忘了等待它,你的编译器会警告你。
当你的调用遇到函数中的await时,你的线程会调高其调用堆栈以查看它是否可以执行其他操作。如果您没有等待,可以继续处理,直到您必须等待。该线程再次上调其调用堆栈,以查看其中一个调用者是否在等待等等。
异步任务ReadDataAsync() { //做一些准备 使用(TextReader textReader = ...) { var myReadTask = textReader.ReadToEndAsync(); //而textReader正在等待信息可用 //你可以做其他事 ProcessSomething();
// after a while you really need the results from the read data,
// so you await for it.
string text = await MyReadTask;
// after the await, the results from ReatToEnd are available
Process(text);
...
有一些规则要遵循:
Task
而不是void Task<TResult>
而不是TResult
Task
。await Task
的结果无效,await Task<TResult>
的结果为TResult
请注意,即使在等待它们之前调用多个异步函数,也不意味着多个线程同步运行这些函数。第一次调用异步函数后的语句在被调用函数开始等待后处理。
async Task DoSomethingAsync()
{
var task1 = ReadAsync(...);
// no await, so next statement processes as soon as ReadAsync starts awaiting
DoSomeThingElse();
var task2 = QueryAsync(...);
// again no await
// now I need results from bothtask1, or from task2:
await Task.WhenAll(new Task[] {task1, task2});
var result1 = Task1.Result;
var result2 = Task2.Result;
Process(result1, result2);
...
通常,所有异步功能都由相同的上下文执行。在实践中,这意味着您可以编程,就好像您的程序是单线程的。这使您的程序外观更容易。
另一篇帮助我理解async-await的文章是Async-Await best practices written by the ever so helpful Stephen Cleary