[英]In the context of ASP.NET, why doesn't Task.Run(…).Result deadlock when calling an async method?
[英]Task.Run(() => Method()); doesn't run the method?
在我的WPF應用程序中:
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using System.Diagnostics;
namespace CloudKey
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
AuthText.Visibility = Visibility.Hidden;
}
private async void button_Click(object sender, RoutedEventArgs e)
{
AuthText.Visibility = Visibility.Visible;
await Task.Run(() => Authenticate());
Task.Factory.StartNew(() => Authenticate());
Task.Run(() => Authenticate());
Authenticate();
}
void Authenticate()
{
//Do Stuff
}
}
}
無論我嘗試以哪種方式調用“通過Tasks
進行Authenticate
,它都不會運行。 我使用Task
錯誤?
使用await(和async)會引發異常:
未處理System.InvalidOperationException消息:mscorlib.dll中發生了'System.InvalidOperationException'類型的未處理異常。其他信息:調用線程無法訪問此對象,因為其他線程擁有它。
僅使用Task.Run或Task.Factory.StartNew會導致完全不執行Authenticate方法。 如果我將斷點添加到Authenticate方法中,則無法實現。
只需使用Authenticate()調用該方法即可運行整個方法,而不會出現問題,但是它將凍結UI,使之成為“ AuthText.Visibility = Visibility.Visible;”。 無用。
老實說,我真的只希望UI更新消息“ Authenticating ...”,然后在單擊按鈕時運行方法中的所有內容。 也許有更簡單的方法做到這一點?
這是參考的工作代碼:
using System;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using System.Diagnostics;
using System.Threading.Tasks;
namespace CloudKey
{
/// <summary>
/// Interaction logic for Page1.xaml
/// </summary>
public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
//private void PasswordBox_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { button_Click } }
AuthText.Visibility = Visibility.Hidden;
}
private void button_Click(object sender, RoutedEventArgs e) //ON CONTINUE BUTTON CLICK
{
AuthText.Visibility = Visibility.Visible;
Task.Run(() => Authenticate());
}
void Authenticate()
{
Dispatcher.Invoke(
() =>
{
//ALL MY CODE HERE;
});
}
}
}
問題是您不等待異步任務完成,因此看起來好像“什么都沒有發生”-實際上確實發生了。 當您調用Task.Run
或Task.Factory.StartNew
,除非您正確處理Task
,否則實際上就是在執行即Task.Run
即Task.Factory.StartNew
。
private async void button_Click(object sender, RoutedEventArgs e)
{
await Task.Run(() => Authenticate()); // Stuff happens
}
void Authenticate()
{
// DO STUFF
}
在上面的示例中,將關鍵字async
添加到事件處理程序允許該方法利用await
關鍵字。 await
關鍵字是所有魔術真正發生的地方……但是它將按您期望的那樣工作,即; “事情發生了”。
當我擊敗斯蒂芬·克雷蒂(Stephen Cleary)時,通常會向人們指向他的博客, 這一博客尤其應為您澄清這一點。
注意
強烈建議不要編寫async void
! 唯一的例外是在示例中將其應用於事件處理程序。 最后,當使用Task
和Task<T>
以及async / await
關鍵字時,請在整個堆棧中使用。 我會更改您的Authenticate
方法以返回一個Task
,例如,可以等待它。 嘗試調用Task.Run
盡可能最低的級別。
private async void button_Click(object sender, RoutedEventArgs e)
{
await Authenticate(); // Stuff happens
}
Task Authenticate()
{
return _authModule.Authenticate();
}
更新資料
根據您的評論,執行以下操作:
private void button_Click(object sender, RoutedEventArgs e)
{
bool authenticated = false;
try
{
AuthText = "Authenticating...";
authenticated = Authenticate(); // Stuff happens
}
finally
{
AuthText = authenticated ? "Authenticated" : "Oops!";
}
}
bool Authenticate()
{
// Return if auth was successful
}
在新線程中修改UI內容時,您需要使用Dispatcher.Invoke
,也可以使用InvokeAsync
private void Button_Click( object sender, RoutedEventArgs e ) { Task.Run( () => Authenticate() ); }
public void Authenticate()
{
Dispatcher.Invoke(
() =>
{
ClickButton.Content = "Text Changed";
} );
}
通過使用Dispatcher
您正在告訴WPF在有權更新您的GUI控件的主線程上運行此代碼塊。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.