等待收集更新

时间:2014-06-27 18:42:30

标签: c# .net-4.5

我需要一些代码来等待项目添加到集合中。线程本身需要等到完成才继续。该项目从另一个线程添加到集合中(从其他地方开始),我无法通知当前线程发生了这种情况。

一个天真的实现可能看起来像(忽略集合上的线程安全等等):

public static List<string> myList = new List<string>();

public void WaitingMethod(string id)
{
    bool waiting = true;
    while (waiting)
    {
        int index = myList.IndexOf(id);
        if (index >= 0)
        {
            waiting = false;
        }
    }

    //thread continues here
}

我相信这会阻塞线程并固定处理器。

实现这一目标的更有效方法是什么?

2 个答案:

答案 0 :(得分:3)

您可以改为使用可观察的集合,并订阅CollectionChanged事件吗?

namespace ConsoleApplication3
{
    using System;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;

    internal class Program
    {
        private static readonly ObservableCollection<string> MyList = new ObservableCollection<string>();

        private static AutoResetEvent resetEvent = new AutoResetEvent(false);
        private static void Main(string[] args)
        {
            Task.Factory.StartNew(
                () =>
                {
                    for (int i = 0; i < 10; i++)
                    {
                        string item = i.ToString("0000");
                        MyList.Add(item);
                        Console.WriteLine(item);
                        Thread.Sleep(1000);
                    }
                });

            MyList.CollectionChanged += (sender, eventArgs) =>
            { if (eventArgs.NewItems.Cast<string>().Any(a => a.Equals("0005"))) resetEvent.Set(); };
            resetEvent.WaitOne();
        }

    }

}

以下是如何控制循环生成要添加到集合中的项目:

namespace ConsoleApplication3
{
    using System;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;

    internal class Program
    {
        #region Static Fields

        private static readonly CancellationTokenSource Cts = new CancellationTokenSource();

        private static readonly ObservableCollection<string> MyList = new ObservableCollection<string>();

        private static readonly AutoResetEvent ResetEvent = new AutoResetEvent(false);

        #endregion

        #region Methods

        private static void Main(string[] args)
        {
            Task task = Task.Factory.StartNew(
                () =>
                {
                    for (int i = 0; i < 10 && !Cts.IsCancellationRequested; i++)
                    {
                        string item = i.ToString("0000");
                        MyList.Add(item);
                        Console.WriteLine(item);
                        Thread.Sleep(100);
                    }
                },
                Cts.Token);
            Task finish = task.ContinueWith(antecedent => { Console.WriteLine("Task finished. Status {0}", antecedent.Status); });
            MyList.CollectionChanged += (sender, eventArgs) =>
            {
                if (eventArgs.NewItems.Cast<string>().Any(a => a.Equals("0005")))
                {
                    Cts.Cancel();
                    ResetEvent.Set();
                }
            };
            ResetEvent.WaitOne();
            Task.WaitAll(finish);
        }

        #endregion
    }
}

答案 1 :(得分:3)

正如其他人所说,使用ObservableCollection。但是,您还需要AutoResetEvent

  1. 在类中,在实例化或程序初始化期间,您使用ObservableCollection注册Collection Changed事件处理程序。调用此方法时,它会调用AutoResetEvent's Set方法来表示集合已更改。

  2. 当你的线程到达需要等待的程度时,你等待AutoResetEvent

  3. 你的其他线程做了它的事情并改变了集合。这会调用您在集合中注册的方法,这会引发AutoResetEvent。这标志着你正在等待醒来的线程。

  4. 这样,等待的线程对CPU没有影响。