如何同步更新我的ListBox

时间:2015-03-06 07:10:42

标签: c# wpf ienumerable

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

 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线程,所有素数使用上面的代码。我正在使用的approch如下,但这不成功。

   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

3 个答案:

答案 0 :(得分:2)

你刚发帖太多了。此代码按预期工作:

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类。此外,不要使用平台的同步机制,而是尝试切换到与平台无关的SynchronizationContext。

您可以将结果发布成一串而不是睡觉线程:

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

答案 1 :(得分:0)

该代码看起来没问题,您只需致电BackgroundWorker.RunWorkerAsync,即可忘记启动您的后台工作人员。

PrimeNumbers = new ObservableCollection<int>();

这一行必须在你的worker之外(或者也在UI线程中调用)。

答案 2 :(得分:-1)

似乎我的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);
        }            
    }