2012-07-02 15 views
5

Funkcjonalnie mają długą listę słów związanych z ListView. Użyj pola TextBox dla znaków, aby przefiltrować listę słów.Jak anulować dowolny bieżący Parallel.ForEach i rozpocząć świeże

Przy każdym nowym char trzeba anulować filtr przetwarzania w tle. Następnie odczekaj 1 sekundę (DispatcherTimer), aby uruchomić świeży filtr równoległy tła.

Wykonaj tę operację za pomocą BackGroundWorker, ale nie możesz przetłumaczyć części cancel-any-processing na Parallel. Zasadniczo potrzebujesz "if (backgroundWorkerFTSfilter.IsBusy) backgroundWorkerFTSfilter.CancelAsync();" równolegle.
Jeśli mam zamiar rozwiązać ten problem, proszę dać mi znać.

private List<FTSword> fTSwordsFiltered = new List<FTSword>(); 
CancellationTokenSource ftsCts = new CancellationTokenSource(); 
ParallelOptions ftspo = new ParallelOptions(); 
// in ctor ftspo.CancellationToken = ftsCts.Token; 

public List<FTSword> FTSwordsFiltered // ListView bound to 
{ 
    get { return fTSwordsFiltered; } 
    set 
    { 
     if (fTSwordsFiltered == value) return; 
     fTSwordsFiltered = value; 
     NotifyPropertyChanged("FTSwordsFiltered"); 
    } 
} 
public string FTSwordFilter   // TextBox bound to 
{ 
    get { return fTSwordFilter; } 
    set 
    { 
     if (value == fTSwordFilter) return; 

     fTSwordFilter = value; 
     NotifyPropertyChanged("FTSwordFilter"); 

     // cancel any filter currently processing 
     ftsCts.Cancel(); // fts filter    
     // with BackgroundWorker this was able to cancel    
     // if (backgroundWorkerFTSfilter.IsBusy) backgroundWorkerFTSfilter.CancelAsync(); 

     dispatcherTimerFTSfilter.Stop(); 
     // wait 1 second and apply filter in background 
     dispatcherTimerFTSfilter.Start(); 
    } 
} 
private void dispatcherTimerFTSfilter_Tick(object sender, EventArgs e) 
{ 
    dispatcherTimerFTSfilter.Stop(); 
    List<FTSword> ftsWords = new List<FTSword>(); 
    //ftsCts = new CancellationTokenSource(); with these two it never cancels 
    //ftspo.CancellationToken = ftsCts.Token; 
    if (!(string.IsNullOrEmpty(FTSwordFilter))) 
    { 
     Task.Factory.StartNew(() => 
     { 

      try 
      { 
       Parallel.ForEach(FTSwords, ftspo, ftsw => 
       { 
        if (ftsw.WordStem.StartsWith(FTSwordFilter)) 
        { 
         ftsWords.Add(ftsw); 
        } 
        ftspo.CancellationToken.ThrowIfCancellationRequested(); 
       }); 
       Thread.Sleep(1000); // so the next key stoke has time 
       FTSwordsFiltered = (List<FTSword>)ftsWords; 
      } 
      catch (OperationCanceledException ei) 
      { 
       // problem is that it is always cancelled from the cancel request before DispatchTimer 
       Debug.WriteLine(ei.Message); 
      } 
      Debug.WriteLine(ftsWords.Count.ToString() + "parallel "); 
     });   
    } 
} 

Odpowiedź z Irman doprowadziły mnie do tego

if (!(string.IsNullOrEmpty(FTSwordFilter))) 
    { 
     string startWorkFilter = FTSwordFilter; 
     Task.Factory.StartNew(() => 
     { 
      try 
      { 
       fTSwordsFilteredCancel = false; 
       Parallel.ForEach(FTSwords, ftspo, (ftsw, loopstate) => 
       { 
        if (ftsw.WordStem.StartsWith(startWorkFilter)) 
        { 
         ftsWords.Add(ftsw); 
        } 
        // Thread.Sleep(1); 
        if (fTSwordsFilteredCancel) 
        { 
         loopstate.Break(); 
        } 
       }); 
       Debug.WriteLine("fTSwordsFilteredCancel " + fTSwordsFilteredCancel.ToString()); 
       FTSwordsFiltered = (List<FTSword>)ftsWords; 
       Debug.WriteLine(ftsWords.Count.ToString() + " parallel " + startWorkFilter);      
      } 
      catch (OperationCanceledException ei) 
      { 
       Debug.WriteLine(ei.Message); 
      } 
     }); 
    } 

bardzo wdzięczny za odpowiedź I i wykorzysta to dla niektórych już uruchomionych zadań lub dłuższej liście, ale mam taką wielką wydajność przeniosłem to do get (wciąż z 1 sekundowym opóźnieniem). Wynikiem jest mniejszy ślad pamięci. Przeciw 800,000 działa w mniej niż 1/10 sekundy.

public IEnumerable<FTSword> FTSwordsFiltered 
{ 
    get 
    { 
     if(string.IsNullOrEmpty(FTSwordFilter) || FTSwordFilter == "*") return FTSwords; 
     return FTSwords.AsParallel().Where(ftsWrd => ftsWrd.WordStem.StartsWith(FTSwordFilter)); 
    } 

Odpowiedz

5

Parallel.ForEach dostarczany jest z obiektem ParallelLoopState. możesz użyć tego obiektu, aby przerwać pętlę.

możesz użyć loopState.Break() lub loopState.Stop() w zależności od wymagań.

zaznacz to.

http://msdn.microsoft.com/en-us/library/dd991486.aspx

+0

Proszę sprawdzić, co moim zdaniem jest prawidłowe użycie loopState.Break() przed oznaczyć jako odpowiedział. (wersja równoległa była tak szybka, że ​​musiałem przespać się w Parallel.ForEach, aby mieć nawet czas na anulowanie) – Paparazzi

+0

Wygląda dobrze. Jeśli nie chcesz używać tokena anulowania, możesz usunąć obiekt opcji równoległej przekazany w wywołaniu Parallel.ForEach. BTW - ParallelOption służy również do ograniczenia liczby procesorów używanych do uruchamiania tego połączenia. –

+0

W ustawieniach ustawiam ftspo.MaxDegreeOfParallelism = System.Environment.ProcessorCount; aby trzymać rzeczy ograniczone. – Paparazzi