C#学习教程:异步等待保持事件发生分享


异步等待保持事件发生

我在C#.NET应用程序中有关于async await的问题。 我实际上是在基于Kinect的应用程序中尝试解决这个问题但是为了帮助我说明,我已经制作了这个类似的例子:

想象一下,我们有一个名为timer1的Timer,它设置了Timer1_Tick事件。 现在,我对该事件采取的唯一操作是使用当前日期时间更新UI。

private void Timer1_Tick(object sender, EventArgs e) { txtTimerValue.Text = DateTime.Now.ToString("hh:mm:ss.FFF", CultureInfo.InvariantCulture); } 

这很简单,我的UI每隔几百分钟更新一次,我很高兴看到时间流逝。

现在想象一下,我也想用同样的方法计算前500个素数,如下:

  private void Timer1_Tick(object sender, EventArgs e) { txtTimerValue.Text = DateTime.Now.ToString("hh:mm:ss.FFF", CultureInfo.InvariantCulture); List primeNumbersList = WorkOutFirstNPrimeNumbers(500); PrintPrimeNumbersToScreen(primeNumbersList); } private List WorkOutFirstNPrimeNumbers(int n) { List primeNumbersList = new List(); txtPrimeAnswers.Clear(); int counter = 1; while (primeNumbersList.Count < n) { if (DetermineIfPrime(counter)) { primeNumbersList.Add(counter); } counter++; } return primeNumbersList; } private bool DetermineIfPrime(int n) { for (int i = 2; i < n; i++) { if (n % i == 0) { return false; } } return true; } private void PrintPrimeNumbersToScreen(List primeNumbersList) { foreach (int primeNumber in primeNumbersList) { txtPrimeAnswers.Text += String.Format("The value {0} is prime rn", primeNumber); } } 

这是我遇到问题的时候。 计算素数的密集方法会阻止事件处理程序运行 – 因此我的计时器文本框现在每30秒左右更新一次。

我的问题是,如何在遵守以下规则的同时解决这个问题:

我试图用async / await做一些事情并使我的素数计算函数返回一个Task>但是没有设法解决我的问题。 Timer1_Tick事件中的await调用似乎仍然阻塞,阻止了进一步执行处理程序。

任何帮助都将很高兴 – 我非常擅长接受正确的答案:)

更新:我非常感谢@sstan能够为这个问题提供一个简洁的解决方案。 但是,我在将这个应用到基于Kinect的真实情况时遇到了麻烦。 由于我有点担心这个问题太具体,我在这里发布了一个新的问题: Kinect Frame Arrived Asynchronous

因此,您希望在不等待结果的情况下启动任务。 当任务完成计算时,它应该更新UI。

首先是关于异步的一些事情,等待你的回答

您的UI在长时间操作期间没有响应的原因是您没有声明事件处理程序是异步的。 查看结果的最简单方法是为按钮创建事件处理程序:

synchronous – 在执行期间阻止UI:

 private void Button1_clicked(object sender, EventArgs e) { List primeNumbersList = WorkOutFirstNPrimeNumbers(500); PrintPrimeNumbersToScreen(primeNumbersList); } 

异步 – UI在执行期间响应:

 private async void Button1_clicked(object sender, EventArgs e) { List primeNumbersList = await Task.Run( () => WorkOutFirstNPrimeNumbers(500)); PrintPrimeNumbersToScreen(primeNumbersList); } 

注意区别:

注意:

您的问题:计算仍然繁忙时报告计时器滴答

问题是您的计时器比计算速度快。 如果在先前的计算未完成时报告新的滴答,您想要什么?

  1. 无论如何,开始新的计算。 这可能导致许multithreading同时进行计算。
  2. 忽略勾号,直到没有计算繁忙
  3. 您也可以选择只让一个任务执行计算,并在完成后立即启动它们。 在这种情况下,计算连续进行

(1)启动任务,但不要等待它。

 private void Button1_clicked(object sender, EventArgs e) { Task.Run ( () => { List primeNumbersList = WorkOutFirstNPrimeNumbers(500); PrintPrimeNumbersToScreen(primeNumbersList); }); } 

(2)如果任务仍然忙,则忽略勾选:

 Task primeCalculationTask = null; private void Button1_clicked(object sender, EventArgs e) { if (primeCalculationTask == null || primeCalculationTask.IsCompleted) { // previous task finished. Stat a new one Task.Run ( () => { List primeNumbersList = WorkOutFirstNPrimeNumbers(500); PrintPrimeNumbersToScreen(primeNumbersList); }); } } 

(3)启动连续计算的任务

 private void StartTask(CancellationToken token) { Task.Run( () => { while (!token.IsCancelRequested) { List primeNumbersList = WorkOutFirstNPrimeNumbers(500); PrintPrimeNumbersToScreen(primeNumbersList); } }) } 

可能不是最好的解决方案,但它会起作用。 您可以创建2个单独的计时器。 您的第一个计时器的Tick事件处理程序只需要处理您的txtTimerValue文本框。 它可以保持原来的方式:

 private void Timer1_Tick(object sender, EventArgs e) { txtTimerValue.Text = DateTime.Now.ToString("hh:mm:ss.FFF", CultureInfo.InvariantCulture); } 

对于第二个计时器的Tick事件处理程序,定义Tick事件处理程序,如下所示:

 private async void Timer2_Tick(object sender, EventArgs e) { timer2.Stop(); // this is needed so the timer stops raising Tick events while this one is being awaited. txtPrimeAnswers.Text = await Task.Run(() => { List primeNumbersList = WorkOutFirstNPrimeNumbers(500); return ConvertPrimeNumbersToString(primeNumbersList); }); timer2.Start(); // ok, now we can keep ticking. } private string ConvertPrimeNumbersToString(List primeNumbersList) { var primeNumberString = new StringBuilder(); foreach (int primeNumber in primeNumbersList) { primeNumberString.AppendFormat("The value {0} is prime rn", primeNumber); } return primeNumberString.ToString(); } // the rest of your methods stay the same... 

您会注意到我将PrintPrimeNumbersToScreen()方法更改为ConvertPrimeNumbersToString() (其余方法保持不变)。 更改的原因是您确实希望最小化UI线程上的工作量。 所以最好从后台线程准备字符串,然后只需对UI线程上的txtPrimeAnswers文本框进行简单的赋值。

编辑:另一种可以与单个计时器一起使用的替代方案

这是另一个想法,但有一个计时器。 这里的想法是你的Tick even处理程序将继续定期执行并每次更新你的计时器值文本框。 但是,如果素数计算已经在后台进行,则事件处理程序将跳过该部分。 否则,它将启动素数计算,并在完成后更新文本框。

 // global variable that is only read/written from UI thread, so no locking is necessary. private bool isCalculatingPrimeNumbers = false; private async void Timer1_Tick(object sender, EventArgs e) { txtTimerValue.Text = DateTime.Now.ToString("hh:mm:ss.FFF", CultureInfo.InvariantCulture); if (!this.isCalculatingPrimeNumbers) { this.isCalculatingPrimeNumbers = true; try { txtPrimeAnswers.Text = await Task.Run(() => { List primeNumbersList = WorkOutFirstNPrimeNumbers(500); return ConvertPrimeNumbersToString(primeNumbersList); }); } finally { this.isCalculatingPrimeNumbers = false; } } } private string ConvertPrimeNumbersToString(List primeNumbersList) { var primeNumberString = new StringBuilder(); foreach (int primeNumber in primeNumbersList) { primeNumberString.AppendFormat("The value {0} is prime rn", primeNumber); } return primeNumberString.ToString(); } // the rest of your methods stay the same... 

您应该避免使用async / await(尽管它们有多好),因为Microsoft的Reactive Framework(Rx) – NuGet要么是“Rx-WinForms”还是“Rx-WPF” – 是一种更好的方法。

这是Windows窗体解决方案所需的代码:

 private void Form1_Load(object sender, EventArgs e) { Observable .Interval(TimeSpan.FromSeconds(0.2)) .Select(x => DateTime.Now.ToString("hh:mm:ss.FFF", CultureInfo.InvariantCulture)) .ObserveOn(this) .Subscribe(x => txtTimerValue.Text = x); txtPrimeAnswers.Text = ""; Observable .Interval(TimeSpan.FromSeconds(0.2)) .Select(n => (int)n + 1) .Where(n => DetermineIfPrime(n)) .Select(n => String.Format("The value {0} is primern", n)) .Take(500) .ObserveOn(this) .Subscribe(x => txtPrimeAnswers.Text += x); } 

而已。 非常简单。 这一切都发生在后台线程上,然后被编组回UI。

以上内容应该是相当自我解释的,但如果您需要进一步解释,请大声说出来。

上述就是C#学习教程:异步等待保持事件发生分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—猴子技术宅(www.ssfiction.com)

本文来自网络收集,不代表猴子技术宅立场,如涉及侵权请点击右边联系管理员删除。

如若转载,请注明出处:https://www.ssfiction.com/ckf/1031775.html

发表评论

邮箱地址不会被公开。 必填项已用*标注