当使用async/await和Task.Run时,不会显示特定的异常
本文关键字:显示 异常 Run async await Task | 更新日期: 2023-09-27 18:02:08
我有一个食谱列表的网页。在我的代码中,我循环遍历页面并下载每个食谱。下面是我正在做的伪代码:
//This is the Recipe class
//The constructor accepts a link to the recipe
//This method scrapes the recipe
public Task ScrapeAsync(){
return Task.Run(async () => {
string WebPage;
WebRequest request = WebRequest.Create(link);
request.Method = "GET";
using (WebResponse response = await request.GetResponseAsync())
using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
WebPage = await reader.ReadToEndAsync();
}
//Non - async code here which is used to scrape the webpage
}
}
我使用Task。因为方法中既有异步代码,也有阻塞代码。
//This is the RecipePage class
//This method scrapes the page
public Task GetRecipeListAsync(){
return Task.Run(async () => {
//I get the page using WebRequest and WebResponse in the same way as above
//Non - async/blocking code here which scrapes the page and finds links to recipes. I do await Recipe.ScrapeAsync() somewhere here.
//And I add the recipe objects to a public list in this class
}
}
在表单中,它循环遍历页面列表并执行await RecipePage.GetRecipeList()
和其他操作。
这里是for循环的位置:
private async void go_Click(object sender, EventArgs e){
for (int i = (int)startingPageNUD.Value; i <= (int)finishingPageNUD.Value; ++i) {
RecipePage page = new RecipePage(page + i);
await page.GetRecipeListAsync();
//Some other code here
}
}
我的问题是,每当一个异常发生在ScrapeAsync
方法,Visual Studio 2013指向Application.Run(new Form1())
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
并告诉我Reflection.TargetInvocationException
已经发生。它没有显示我的代码中的实际异常。例如,如果我在代码中得到NullReferenceException
,它不会显示这一点。正因为如此,我不得不编写异步和非异步代码,并使用非异步代码进行调试。有办法解决这个问题吗?
我还有一个问题。我是否以正确的方式使用async/await和Task ?
我是否以正确的方式使用async/await和Task ?
相当接近。我认为在这种情况下不需要Task.Run
(它应该只用于将cpu密集型操作移出UI线程,并且HTML抓取通常足够快,可以留在UI上而不会产生任何不利影响)。
async Task
方法尽可能返回值,而不是修改成员变量作为副作用。在您的示例中,请考虑让ScrapeAsync
返回它自己的列表,而不是更新共享列表,并让调用代码进行列表合并。
关于您的异常,TargetInvocationException
将具有InnerException
的详细信息。async void
事件处理程序中的异常与常规void
事件处理程序中的异常的管理方式几乎相同:如果一个人转义事件处理程序,那么它将进入应用程序主循环。如果你不想这样,你必须捕获它(对于同步和异步处理程序)。
好的。感谢你的编辑,我现在可以回答了。
你的问题是这个函数:
private async void go_Click(object sender, EventArgs e){
for (int i = (int)startingPageNUD.Value; i <= (int)finishingPageNUD.Value; ++i) {
RecipePage page = new RecipePage(page + i);
await page.GetRecipeListAsync();
//Some other code here
}
}
问题是,函数一旦到达await
,就返回到调用它的对象。现在,如果page.GetRecipeListAsync();
抛出异常,这个异常将在延续处理程序中抛出。此处理程序在UI的任务队列中执行。在那里抛出的异常会导致UI的任务循环崩溃,这会产生各种奇怪的效果,包括奇怪的异常。
在async void
函数中,您应该始终通过将所有代码包装到try
…catch
中来处理任何传入的异常。如果您崩溃了应用程序,如果那里发生了异常,这是您的决定。
事情的一般工作方式是,在Task
和async
函数中抛出的任何异常都将由await
再次抛出。但是async void
函数在任何地方都没有await
,所以这不起作用。
关于async
。我不认为你需要包装每个功能到一个Task
,但你可以这样做。通常你不需要像这样强迫它进入背景
将getRecipeList代码封装在try/catch中。
在catch代码中,获取异常消息和堆栈跟踪,并将它们分配给Recipe类的变量,当操作完成时,您将在主线程中测试/显示这些变量。
private string TheException = "" ;
public Task GetRecipeListAsync()
{
TheException = "" ;
Task result = Task.Run(async () =>
{
try
{
//I get the page using WebRequest and WebResponse in the same way as above
...
}
catch (Exception Ex)
}
if (TheException!="") MessageBox.show(TheException) ;
// or Throw an exception with
return result ;
}
你也可以在GoClick过程中测试"TheExceptio"