2015-03-06 3 views
0

Я вычисление простых чисел м.т. два чисел, используя следующий кодКак я могу постоянно обновлять свой ListBox синхронно

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(); 
    } 

но этот подход замораживает мой интерфейс. Я хочу, чтобы иметь результат непрерывно идущий от метода GetPrimes и продолжайте добавлять к моему listboz

+0

Вы можете просто выполнить всю работу над рабочим потоком, а затем выполнить команду Invoke() в элементе управления со всей обновленной информацией. Или вы можете сделать то же самое с кодом, который у вас есть, так как вы хотите регулярно его обновлять, сначала получите все номера, а затем выполните begininvoke, а не делайте это для каждого номера, – SomeNickName

+1

Простой. Вы обновляете пользовательский интерфейс очень часто. Количество работ, выполненных в рабочей нити, сравнительно меньше, чем то, что делает поток пользовательского интерфейса. Вы можете обновить все сразу или сделать это в пакете. –

+0

@SriramSakthivel Большое спасибо. Вы помогли мне добраться до решения, которое я стану ответом – Vikram

ответ

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, если вы застряли с более старыми версиями рамок.

+0

Я думаю, что вам вообще не нужны '_locker' и' lock (_locker) '.В противном случае этот ответ хорош +1 –

+0

@Sriram Sakthivel, замок определенно необходим здесь, поскольку 'Add' не является потокобезопасным. Я проверил его, и я получаю исключение, если блокировка не представлена. – aush

+0

Нет, вам это не нужно. потому что только один поток будет называть его (поток пользовательского интерфейса). Итак, зачем вам его блокировать? Если вы получите исключение, это, вероятно, связано с какой-то другой причиной. Я думаю, прежде чем у вас не было 'primesBuffer.ToList()', если это правда, вы получите исключение. –

0

Этот код выглядит нормально, вы просто забыли начала ваш фон рабочего по телефону BackgroundWorker.RunWorkerAsync.

PrimeNumbers = new ObservableCollection<int>(); 

Эта строка должна находиться за пределами вашего рабочего (или быть вызвана в потоке пользовательского интерфейса).

+1

Если он не запустит рабочего, как он замораживает пользовательский интерфейс? –

+0

Старт есть, я просто пропустил, чтобы упомянуть здесь. После запуска пользовательский интерфейс замерзает. – Vikram

+0

Ну, вы обновляете * много *, так что вполне может показаться, что пользовательский интерфейс застыл. Тем не менее, если вы начинаете, можете ли вы отправить свой * настоящий * код? – nvoigt

-1

Похоже, что мой пользовательский интерфейс очень часто обрабатывался, поэтому я использовал задержку в 1 секунду на моей рабочей рабочей ветке. Это помогло мне достичь моей функциональность

частных недействительного _worker_DoWork (объект отправителя, DoWorkEventArgs е) {

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

Нет, пожалуйста. Не делай этого. Почему вы помещаете нить в состояние сна без причины.? Лучше всего будет обновлять пользовательский интерфейс в пакетном режиме. Скажите каждые 10000 элементов. –

+0

@SriramSakthivel Я не уверен, как это сделать. Не могли бы вы дать мне код ссылки. Большое спасибо – Vikram

+0

@Vikram, вы можете увидеть реализацию в моем втором примере кода. – aush

Смежные вопросы