我怎样才能保持同步更新我的列表框

How can I keep updating my ListBox synchronously

我正在使用以下代码计算两个数字之间的素数

 private static IEnumerable<int> GetPrimes(int from, int to)
    {
        for (int i = from; i <= to; i++)
        {
            bool isPrime = true;
            int limit = (int)Math.Sqrt(i);
            for (int j = 2; j <= limit; j++)
                if (i % j == 0)
                {
                    isPrime = false;
                    break;
                }
            if (isPrime)
            {
                yield return i;
            }
        }       
    }

我想更新我的列表框而不阻塞我的 UI 线程,所有素数都使用上面的代码。我正在使用的方法如下,但这没有用。

   public MainWindow()
    {
        InitializeComponent();
        _worker = new BackgroundWorker();
        _worker.DoWork += _worker_DoWork;
        this.DataContext = this;

    } 

  private void _worker_DoWork(object sender, DoWorkEventArgs e)
    {
        PrimeNumbers = new ObservableCollection<int>();
        foreach (var item in GetPrimes(1, 10000000))
        {               
            Dispatcher.BeginInvoke(new Action<int>(Test), item);
        }            
    }

    private void Test(int obj)
    {
        PrimeNumbers.Add(obj);
    }

 public ObservableCollection<int> PrimeNumbers 
    {
        get
        {
            return primeNumbers;
        }
        set
        {
            primeNumbers = value;
            OnPropertyChanged("PrimeNumbers");

        }
    }

 private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        _worker.RunWorkerAsync();
    }

但是这种方法冻结了我的 UI。我希望从 GetPrimes 方法中不断得到结果,并不断添加到我的 listboz

该代码看起来不错,您只是忘记通过调用 BackgroundWorker.RunWorkerAsync.

启动 您的后台工作程序
PrimeNumbers = new ObservableCollection<int>();

这一行需要在您的工作程序之外(或者也在 UI 线程中被调用)。

我的 UI 更新似乎非常频繁,所以我在我的后台工作线程上使用了 1 秒的延迟。它帮助我实现了我的功能

private void _worker_DoWork(对象发送者, DoWorkEventArgs e) {

        foreach (var item in GetPrimes(1, 1000000))
        {
            Thread.Sleep(1000);
            Dispatcher.BeginInvoke(new Action<int>(Test), item);
        }            
    }

你只是 post 太多了。此代码按预期工作:

public partial class MainWindow
{
    public ObservableCollection<int> PrimeNumbers { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        PrimeNumbers = new ObservableCollection<int>();
    }

    private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
    {
        PrintPrimes(PrimeNumbers.Add, 1, 10000000, SynchronizationContext.Current);
    }

    private static void PrintPrimes(Action<int> action, int from, int to, 
                                    SynchronizationContext syncContext)
    {
        Task.Run(() =>
        {
            for (var i = from; i <= to; i++)
            {
                var isPrime = true;
                var limit = (int) Math.Sqrt(i);
                for (var j = 2; j <= limit; j++)
                {
                    if (i%j == 0)
                    {
                        isPrime = false;
                        break;
                    }
                }
                if (isPrime)
                {
                    syncContext.Post(state => action((int)state), i);
                    Thread.Sleep(1);
                }
            }
        });
    }
}

考虑避免使用旧的 BackgroundWorker class。此外,不要使用平台的同步机制,而是尝试切换到平台独立的 SynchronizationContext。

您可以 post 成串地获得结果,而不是休眠一个线程:

public partial class MainWindow
{
    public ObservableCollection<int> PrimeNumbers { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        PrimeNumbers = new ObservableCollection<int>();
    }

    private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
    {
        PrintPrimes(items => items.ForEach(PrimeNumbers.Add), 
                    1, 10000000, SynchronizationContext.Current);
    }

    private static void PrintPrimes(Action<List<int>> action, int from, int to, 
                                    SynchronizationContext syncContext)
    {
        Task.Run(() =>
        {
            var primesBuffer = new List<int>();
            for (var i = from; i <= to; i++)
            {
                var isPrime = true;
                var limit = (int) Math.Sqrt(i);
                for (var j = 2; j <= limit; j++)
                {
                    if (i%j == 0)
                    {
                        isPrime = false;
                        break;
                    }
                }
                if (isPrime)
                {
                    primesBuffer.Add(i);
                    if (primesBuffer.Count >= 1000)
                    {
                        syncContext.Post(state => action((List<int>) state), 
                                         primesBuffer.ToList());
                        primesBuffer.Clear();
                    }
                }
            }
        });
    }
}

如果您受困于旧版本的框架,您可以使用 Thread 而不是 Task.Run