【问题标题】:Async Await Loop/Math problems异步等待循环/数学问题
【发布时间】:2015-10-02 04:54:16
【问题描述】:

我正在制作一个小程序来练习使用 WPF 和 Async/Await 进行多线程处理,该程序的作用是:

  1. 找出两个数字“a”和“b”之间的所有质数,并将它们输出到一个名为“Prime1”的文本框中。
  2. 同时在另一个任务中,找出“c”和“d”之间的所有质数,并将它们输出到一个名为“Prime2”的文本框中。
  3. 窗口中的一个按钮将允许用户单击它,它会跟踪它被单击的次数,而其他两个任务会查找素数,以演示异步操作。

代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WPF_Asynch_Project
{
    public partial class MainWindow : Window
    {
        public int ClickAmount = 0;
        public MainWindow()
        {
            InitializeComponent();
            DelegationIsAwesome();
        }

        private void Test_Click(object sender, RoutedEventArgs e)
        {
            ClickAmount++;
            MessageBox.Show("You clicked me " + ClickAmount.ToString() + " times!");
        }

        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
        }

        private async void DelegationIsAwesome()
        {
            Task enumtask = new Task(() => FindPrimes(100000, 100000000));
            Task[] enumall = new Task[2];
            enumall[0] = enumtask;
            enumall[1] = new Task(() => FindPrimes2(1000, 10000));
            enumall.ToList().ForEach(t => t.Start());
            await Task.WhenAll(enumall).ConfigureAwait(false);
        }

        private void FindPrimes(long lower, long upper)
        {
            for (long i = lower; i < upper; i++)
            {

                long primeornot = 1;
                for (long q = 2; q < i; q++)
                {
                    if (i % q == 0)
                    {
                        primeornot = 0;
                    }
                }
                if (primeornot == 1)
                {
                    System.Threading.Thread.Sleep(6);
                    Prime1.Dispatcher.BeginInvoke(
                        (Action)(()=>{ Prime1.Text += i.ToString() + ", "; }));
                }
            }
        }

        private void FindPrimes2(int lower, long upper)
        {
            for (int i = lower; i < upper; i++)
            {
                int primeornot = 1;
                for (int q = 2; q < i; q++)
                {
                    if (i % q == 0)
                    {
                        primeornot = 0;
                    }
                }
                if (primeornot == 1)
                {
                    System.Threading.Thread.Sleep(5);
                    Prime2.Dispatcher.BeginInvoke(
                        (Action)(() => { Prime2.Text += i.ToString() + ", "; }));
                }
            }
        }
    }
}

但是我得到了奇怪的结果。以下为节目截图:

显然素数查找方法的输出是不正确的。但为什么它会不断重复同样的数字呢?它有时也会吐出一个等于 UpperBound 的数字,即使 "i" 永远不应等于或大于 UpperBound。

我的输出发生了什么,我该如何解决?

【问题讨论】:

  • 顺便说一句,您似乎正在使用值为 0 或 1 的 int 变量来表示 false 和 true...为什么不直接使用 bool 呢?您的代码将更易于阅读。
  • @JonSkeet 我多年前养成的旧坏习惯,令人惊讶的是,没有人叫我出来。一个非常好的建议。
  • 您可以考虑将循环“for (int q = 2; q
  • @cscmh99 我昨天确实有那个,但是由于 Jon 指出的 BeginInvoke 的问题,它导致了很多意想不到的行为。但是,现在它已经修复了,我确实使用 Math.Ceiling(Math.Sqrt(i)) 而不仅仅是“i”。

标签: c# wpf multithreading asynchronous async-await


【解决方案1】:

这与 async/await 无关,真的。

你在这里打电话给BeginInvoke

Prime1.Dispatcher.BeginInvoke(
    (Action)(()=>{ Prime1.Text += i.ToString() + ", "; }));

...并且您的 lambda 表达式使用i,这意味着它将在委托执行时附加i 的当前值。当您调用BeginInvoke 时,这不一定是i 的值。

如果要捕获(而不是变量),基本上每次都需要实例化一个新变量。您不妨将其转换为字符串:

string textToAppend = i + ", ";
// No need for braces here...
Prime1.Dispatcher.BeginInvoke((Action)(() => Prime1.Text += textToAppend));

因为您已经在循环内声明了变量 textToAppend,所以每次迭代都会创建一个委托来捕获一个单独的变量。

您需要在两种方法中都这样做。

【讨论】:

    猜你喜欢
    • 2021-07-17
    • 2020-04-20
    • 1970-01-01
    • 1970-01-01
    • 2019-07-02
    • 2021-10-11
    • 1970-01-01
    • 2018-12-09
    • 2021-04-15
    相关资源
    最近更新 更多