Partea cea mai buna la async/await este ca acest cod se scrie usor .Usurinta aceasta vine de la un State Machine ( vezi text sau video ) si care aduce penalitati de performanta daca nu e folosit cum trebuie (https://ayende.com/blog/174689/the-cost-of-the-async-state-machine )
Vreau sa discut despre un async/ await specific, pe Windows_Closing din WPF. Trebuia sa chem o functie async . Am zis, nimic mai usor:
private async void MyWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { await MyFunc(); }
Acolo, desi pui async pe eveniment si await pe un functie ce ruleaza async, totusi aplicatia se inchide. De ce? As vrea sa stiu – probabil se inchide pe un thread separat.
Am zis, ok , pun e.Cancel=true la inceput si apoi pe false.
private async void MyWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { e.Cancel=true; await MyFunc(); e.Cancel=false; }
Iar nu merge . Nu se mai inchidea.
Am zis, ok, il pacalesc cu scot async de pe eveniment si pun .Result()
private async void MyWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { var t= MyFunc().Result; }
Acum se bloca ( configureAwait, Cleary , https://msdn.microsoft.com/en-us/magazine/dn605875.aspx )
In disperare de cauza , am revenit la old good threads:
bool Cancel; private void MyWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { this.Title = "aaaa"; var _thread = new Thread(new ThreadStart(() => { Cancel = WaitMe(); })); _thread.Start(); _thread.Join(); e.Cancel = Cancel; } bool WaitMe() { var x = WaitOneMinute().Result; return !x; } async Task<bool> WaitOneMinute() { await Task.Delay(5 * 1000); var res=MessageBox.Show("Close ?","Close application?",MessageBoxButton.OKCancel); return res == MessageBoxResult.OK; }
Si asa a mers…
Cred ca ar fi interesant de implementat si sa rulezi un task pe event, care nu blocheaza UI-ul.
Pai … cred ca am dat solutia…
Pana la urma async-await nu e asa simplu cum pare. Imi placea mai mult BeginInvoke-ul, care era mult mai simplu. Event handlerele nu pot fi await-uite. Sunt doar lansate si uitate. De exemplu codul de mai jos afiseaza:
Click
MyEvent1
MyEvent2
using System;
using System.Threading.Tasks;
using System.Windows;
namespace WpfApplication3
{
public partial class MainWindow : Window
{
event EventHandler MyEvent;
public MainWindow()
{
InitializeComponent();
MyEvent += MainWindow_MyEvent1;
MyEvent += MainWindow_MyEvent2;
MyButton.Click += MyButton_Click;
}
private async void MainWindow_MyEvent1(object sender, EventArgs e)
{
await Task.Delay(1);
MyTextBlock.Text += “MyEvent1\r\n”;
}
private async void MainWindow_MyEvent2(object sender, EventArgs e)
{
await Task.Delay(1);
MyTextBlock.Text += “MyEvent2\r\n”;
}
private void MyButton_Click(object sender, RoutedEventArgs e)
{
MyEvent(this, EventArgs.Empty);
MyTextBlock.Text += “Click\r\n”;
}
}
}
Metoda veche, care s-ar putea sa functioneze de pe vremea lui Ceausescu:
using System.ComponentModel;
using System.Windows;
namespace WpfApplication3
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Closing += MainWindow_Closing;
}
private void MainWindow_Closing(object sender, CancelEventArgs e)
{
Dispatcher.Invoke(() =>
e.Cancel = MessageBox.Show(“Close?”, “Close”,
MessageBoxButton.OKCancel) == MessageBoxResult.Cancel);
}
}
}
For non-blocking you can cancel and retrigger the closing.
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
namespace WpfApplication3
{
public partial class MainWindow : Window
{
bool closing;
public MainWindow()
{
InitializeComponent();
Closing += MainWindow_Closing;
}
void MainWindow_Closing(object sender, CancelEventArgs e)
{
if (!closing)
{
closing = true;
e.Cancel = true;
BeforeClosingAsync();
}
}
async void BeforeClosingAsync()
{
await Task.Delay(3000);
Close();
}
}
}