我有一个小应用程序,它使用BackgroundWorker随时处理IEnumerator<T> list
。
代码基本上是这样的:
while(true){
foreach(T item in list){
// Process each item and send process
// Add an object in child List ( List<T1> item.Result )
}
Thread.Sleep(500);
}
现在我有一个按钮和一个文本框,它将直接添加到IEnumerator。
问题是,在我添加按钮后,后台工作人员继续处理它当前处理的项目,但在完成该项目后将停止。它不会继续。
如何安全地将项目添加到列表中而不影响后台工作者?在后台工作者旁边也会添加对象。应该解决这个问题的方法是什么?
谢谢
答案 0 :(得分:6)
让后台工作程序迭代原始列表的副本,而不是列表本身。
while (true)
{
foreach (T item in new List<T>( list ))
{
....
}
Thread.Sleep(500);
}
如果在枚举时尝试修改集合,枚举器将抛出异常。来自docs:
只要收集仍然存在,枚举器仍然有效 不变。如果对集合进行了更改,例如添加, 修改或删除元素,枚举器是不可恢复的 无效,下次调用MoveNext或Reset会抛出一个 InvalidOperationException异常。如果在之间修改了集合 MoveNext和Current,Current返回它设置的元素, 即使枚举器已经失效。
答案 1 :(得分:2)
你应该首先学习有关多线程编程的基础知识,所以,在这里。
尝试一下这个:
// shared queue
ConcurrentQueue<T> queue = new ConcurrentQueue<T>();
// shared wait handle
AutoResetEvent autoEvent = new AutoResetEvent();
这里的队列比列表更好,因为它允许你添加和删除元素而不用担心当前元素的索引 - 你只需Enqueue()
个项目,而且{{1另一方面。使用来自Dequeue()
命名空间的类,它会自动为您处理线程安全访问(并且由于复杂的原因,您可能希望稍后阅读,比简单的System.Collections.Concurrent
更快。)
现在,前台线程:
lock()
这里的魔法部分是我们// schedule the work
queue.Enqueue(itemOfWork);
// and wake up our worker
autoEvent.Set();
上调用的Set()
(是的,WaitHandle
是AutoResetEvent
的实现)。它唤醒了一直在等待同步事件触发的单个线程,而不使用像WaitHandle
这样丑陋的结构。对Thread.Sleep()
的调用几乎总是多线程代码中出错的标志!
好的,最后一部分 - 工作者线程。这里没有太多变化:
Sleep()
答案 2 :(得分:1)
您可能需要使用“lock”关键字来防止从两个代码位置同时访问共享的“list”变量。
http://msdn.microsoft.com/en-us/library/c5kehkcz(v=vs.71).aspx