H W*_*H W 2 .net c# multithreading asynchronous async-await
显然我还不明白async/await,以下基本的例子已经引起了一些麻烦:
出于测试目的,我创建了一个代表我的UI的窗口,我想要启动一个异步方法,该方法在窗口打开时在后台运行.我Listview在我的窗口添加了一个来测试UI是否响应.
当我执行以下代码时,发生了两件我不理解的事情:
ListView展示了CustomObjects我在构造函数中定义的内容.因为我无法在构造函数中使用await关键字,所以我期望调用GetWebResponseAsync().ConfigureAwait(false)会阻塞我的UI-Thread并产生死锁(就像用GetWebResponseAsync().Wait()正在进行的替换调用一样).似乎ConfigureAwait(false)已经让我的方法在另一个线程上运行,即使我没有将它作为任务运行或等待它?Async方法运行时冻结.这实际上并不意外,但如果我考虑先前的观察结果,当我执行我的异步方法时,Listview显然可以从代码中访问,这会让我感到困惑.这是不是意味着我的UI-Thread没有被阻止,因此我应该能够与UI进行交互?由于我被困在这里,我还想知道如何在构造函数中正确调用我的异步方法.如果我使用await GetWebResponseAsync().ConfigureAwait(false);它不编译,因为我的构造函数没有async关键字.但是我现在知道我不应该使用async void(即使我尝试使我的构造函数成为一个async void方法,它也不会编译因为member names can not be the same as their enclosing type).
public partial class TitleWindow : Window
{
public TitleWindow()
{
InitializeComponent();
// I can not use this because the constructor is not async.
//Task tempTask = await GetWebResponseAsync().ConfigureAwait(false);
GetWebResponseAsync().ConfigureAwait(false);
//This should in theory test if my UI-Thread is blocked?!
List<CustomObject> items = new List<CustomObject>();
items.Add(new CustomObject() { Title = "CustomTitle", Year = 2100});
items.Add(new CustomObject() { Title = "CustomTitle2", Year = 2015});
lvTitles.ItemsSource = items;
}
public async Task GetWebResponseAsync(){
WebRequest request = WebRequest.Create("http://www.google.com");
request.Credentials = CredentialCache.DefaultCredentials;
WebResponse response = await request.GetResponseAsync();
//Output for test purposes.
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = await reader.ReadToEndAsync();
Console.WriteLine(responseFromServer);
return;
}
}
Run Code Online (Sandbox Code Playgroud)
更新:
Yuval Itzchakovs答案对我来说非常好.
此模式(取自Yuval Itzchakovs答案中的链接)或两者的组合似乎是更复杂场景的方式.这提供了一种非常舒适的可能性,以便稍后通过等待我的初始化属性来完成构造函数的异步代码.
public partial class TitleWindow : Window, IAsyncInitialization
{
public Task Initialization{get; private set;}
public TitleWindow()
{
InitializeComponent();
Initialization = GetWebResponseAsync();
}
public async Task GetWebResponseAsync(){
//unchanged
}
Run Code Online (Sandbox Code Playgroud)
似乎ConfigureAwait(false)已经使我的方法在另一个线程上运行,即使我没有将它作为任务运行或等待它?
那是不对的.该方法将同步运行,直到达到第一个await关键字.在此之后,因为你不使用ConfigureAwait(false)的GetWebResponseAsync,它会元帅它的延续再次返回到UI线程,这可能是为什么你看到你的UI会被卡住.
这是不是意味着我的UI-Thread没有被阻止,因此我应该能够与UI进行交互?
再一次,没有.async方法没有在后台线程上运行,它在UI线程上使用.当该方法运行其同步部分(例如Stream dataStream = response.GetResponseStream();)时,它仍然在UI线程上执行.
从构造函数调用异步方法自然不起作用,因为构造函数不是异步的(Stephan Cleary有一篇关于它的好博文).
您可以做的是使用附加到窗口加载后触发的事件,例如Loaded事件,您可以在其中正确执行异步方法:
this.Loaded += OnWindowLoaded;
Run Code Online (Sandbox Code Playgroud)
然后你可以正确地await:
private async void OnWindowLoaded(object sender, RoutedEventArgs e)
{
await GetWebResponseAsync().ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)
请注意,如果内部不需要同步上下文GetWebResponseAsync,您还可以使用ConfigureAwait(false)并节省同步上下文封送的开销.