【问题标题】:How To Cancel Any Current Parallel.ForEach And Start Fresh如何取消任何当前的 Parallel.ForEach 并重新开始
【发布时间】:2012-07-02 19:32:12
【问题描述】:

功能上有一个长长的单词列表绑定到 ListView。使用 TextBox for chars 过滤单词列表。

任何新的字符都需要取消任何处理后台过滤器。然后等待 1 秒 (DispatcherTimer) 以启动新的后台并行过滤器。

使用 BackGroundWorker 进行这项工作,但无法将 cancel-any-processing 部分转换为 Parallel。 基本上需要“if (backgroundWorkerFTSfilter.IsBusy) backgroundWorkerFTSfilter.CancelAsync();”并行。
如果我要解决这个问题,请告诉我。

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

Irman 的回答让我明白了这一点

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

非常感谢您的回答,我会将其用于一些运行时间更长的任务或更长的列表,但性能如此出色,因此我将其移至 get(仍有 1 秒延迟)。导致更小的内存占用。针对 800,000,它的运行时间不到 1/10 秒。

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

【问题讨论】:

    标签: .net parallel.foreach


    【解决方案1】:

    Parallel.ForEach 带有 ParallelLoopState 对象。你可以使用这个对象来打破循环。

    您可以根据自己的要求使用loopState.Break()loopState.Stop()

    检查一下。

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

    【讨论】:

    • 在我标记为已回答之前,请检查我认为 loopState.Break() 的正确用法。 (并行版本太快了,我不得不在 Parallel.ForEach 中休眠,甚至有时间取消)
    • 看起来不错。如果您不想使用 Cancellation Token,您可以删除在 Parallel.ForEach 调用中传递的并行选项对象。 BTW - ParallelOption 还用于限制用于运行此调用的处理器数量。
    • 在ctor中我设置了 ftspo.MaxDegreeOfParallelism = System.Environment.ProcessorCount;让事情受到限制。
    猜你喜欢
    • 2021-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-18
    • 1970-01-01
    • 2020-03-09
    • 1970-01-01
    • 2021-10-23
    相关资源
    最近更新 更多