猿问

是否有可能等待事件而不是另一个异步方法?

是否有可能等待事件而不是另一个异步方法?

在我的C#/ XAML metro应用程序中,有一个启动长时间运行过程的按钮。所以,按照建议,我使用async / await来确保UI线程不被阻止:

private async void Button_Click_1(object sender, RoutedEventArgs e) {
     await GetResults();}private async Task GetResults(){ 
     // Do lot of complex stuff that takes a long time
     // (e.g. contact some web services)
  ...}

偶尔,GetResults内发生的事情需要额外的用户输入才能继续。为简单起见,假设用户只需单击“继续”按钮即可。

我的问题是:如何以等待点击另一个按钮等事件的方式暂停GetResults的执行?

这是实现我正在寻找的东西的一种丑陋方式:“继续”按钮的事件处理程序设置了一个标志......

private bool _continue = false;private void buttonContinue_Click(object sender, RoutedEventArgs e){
    _continue = true;}

...和GetResults定期轮询它:

 buttonContinue.Visibility = Visibility.Visible;
 while (!_continue) await Task.Delay(100);  // poll _continue every 100ms
 buttonContinue.Visibility = Visibility.Collapsed;

民意调查显然非常糟糕(忙碌的等待/浪费周期),我正在寻找基于事件的东西。

有任何想法吗?

顺便说一下,在这个简化的例子中,一个解决方案当然是将GetResults()分成两部分,从开始按钮调用第一部分,从继续按钮调用第二部分。实际上,GetResults中发生的事情更复杂,并且在执行中的不同点可能需要不同类型的用户输入。因此,将逻辑分解为多种方法将是非常重要的。


九州编程
浏览 465回答 3
3回答

温温酱

如果你有一件不寻常的事情await,那么最简单的答案往往是TaskCompletionSource(或者是一些async基于原始的原语TaskCompletionSource)。在这种情况下,您的需求非常简单,因此您可以直接使用TaskCompletionSource:private&nbsp;TaskCompletionSource<object>&nbsp;continueClicked;private&nbsp;async&nbsp;void&nbsp;Button_Click_1(object&nbsp;sender,&nbsp;RoutedEventArgs&nbsp;e)&nbsp;{ &nbsp;&nbsp;//&nbsp;Note:&nbsp;You&nbsp;probably&nbsp;want&nbsp;to&nbsp;disable&nbsp;this&nbsp;button&nbsp;while&nbsp;"in&nbsp;progress"&nbsp;so&nbsp;the &nbsp;&nbsp;//&nbsp;&nbsp;user&nbsp;can't&nbsp;click&nbsp;it&nbsp;twice. &nbsp;&nbsp;await&nbsp;GetResults(); &nbsp;&nbsp;//&nbsp;And&nbsp;re-enable&nbsp;the&nbsp;button&nbsp;here,&nbsp;possibly&nbsp;in&nbsp;a&nbsp;finally&nbsp;block.}private&nbsp;async&nbsp;Task&nbsp;GetResults(){&nbsp; &nbsp;&nbsp;//&nbsp;Do&nbsp;lot&nbsp;of&nbsp;complex&nbsp;stuff&nbsp;that&nbsp;takes&nbsp;a&nbsp;long&nbsp;time &nbsp;&nbsp;//&nbsp;(e.g.&nbsp;contact&nbsp;some&nbsp;web&nbsp;services) &nbsp;&nbsp;//&nbsp;Wait&nbsp;for&nbsp;the&nbsp;user&nbsp;to&nbsp;click&nbsp;Continue. &nbsp;&nbsp;continueClicked&nbsp;=&nbsp;new&nbsp;TaskCompletionSource<object>(); &nbsp;&nbsp;buttonContinue.Visibility&nbsp;=&nbsp;Visibility.Visible; &nbsp;&nbsp;await&nbsp;continueClicked.Task; &nbsp;&nbsp;buttonContinue.Visibility&nbsp;=&nbsp;Visibility.Collapsed; &nbsp;&nbsp;//&nbsp;More&nbsp;work...}private&nbsp;void&nbsp;buttonContinue_Click(object&nbsp;sender,&nbsp;RoutedEventArgs&nbsp;e){ &nbsp;&nbsp;if&nbsp;(continueClicked&nbsp;!=&nbsp;null) &nbsp;&nbsp;&nbsp;&nbsp;continueClicked.TrySetResult(null);}逻辑上,TaskCompletionSource就像一个async&nbsp;ManualResetEvent,除了你只能“设置”事件一次,事件可以有一个“结果”(在这种情况下,我们没有使用它,所以我们只是设置结果null)。

慕虎7371278

这是我使用的实用程序类:public class AsyncEventListener{&nbsp; &nbsp; private readonly Func<bool> _predicate;&nbsp; &nbsp; public AsyncEventListener() : this(() => true)&nbsp; &nbsp; {&nbsp; &nbsp; }&nbsp; &nbsp; public AsyncEventListener(Func<bool> predicate)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; _predicate = predicate;&nbsp; &nbsp; &nbsp; &nbsp; Successfully = new Task(() => { });&nbsp; &nbsp; }&nbsp; &nbsp; public void Listen(object sender, EventArgs eventArgs)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; if (!Successfully.IsCompleted && _predicate.Invoke())&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Successfully.RunSynchronously();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; public Task Successfully { get; }}以下是我如何使用它:var itChanged = new AsyncEventListener();someObject.PropertyChanged += itChanged.Listen;// ... make it change ...await itChanged.Successfully;someObject.PropertyChanged -= itChanged.Listen;
随时随地看视频慕课网APP
我要回答