我需要一些代码来等待项目添加到集合中。线程本身需要等到完成才继续。该项目从另一个线程添加到集合中(从其他地方开始),我无法通知当前线程发生了这种情况。
一个天真的实现可能看起来像(忽略集合上的线程安全等等):
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
}
我相信这会阻塞线程并固定处理器。
实现这一目标的更有效方法是什么?
答案 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
:
在类中,在实例化或程序初始化期间,您使用ObservableCollection
注册Collection Changed事件处理程序。调用此方法时,它会调用AutoResetEvent's Set
方法来表示集合已更改。
当你的线程到达需要等待的程度时,你等待AutoResetEvent
。
你的其他线程做了它的事情并改变了集合。这会调用您在集合中注册的方法,这会引发AutoResetEvent
。这标志着你正在等待醒来的线程。
这样,等待的线程对CPU没有影响。