C#:如何使IEnumerable <T>线程安全?
说我有这个简单的方法:
public IEnumerable<uint> GetNumbers() { uint n = 0; while(n < 100) yield return n++; }
你将如何使这个线程安全? 由此我的意思是你会得到一次枚举,并有多个线程处理所有的数字,没有任何人得到重复。
我认为一个锁需要用在某个地方,但是这个锁必须在哪里才能使迭代器块成为线程安全的呢? 什么,一般来说,你需要记住,如果你想要一个线程安全的IEnumerable<T>
? 或者,而是我想这将是一个线程安全的IEnumerator<T>
…?
这样做存在固有的问题,因为IEnumerator<T>
具有MoveNext()
和Current
。 你真的想要一个单一的电话,如:
bool TryMoveNext(out T value)
在这一点上,你可以primefaces地移动到下一个元素,并获得一个值。 实现这一点,仍然能够使用yield
可能会很棘手…我会考虑一下。 我认为你需要将“非线程安全”迭代器封装在一个线程安全的迭代器中,这个线程安全的执行MoveNext()
和Current
来实现上面显示的接口。 我不知道如何将这个接口包装回IEnumerator<T>
以便你可以在foreach
使用它,但是…
如果您使用.NET 4.0,并行扩展可能能够帮助您 – 您需要解释更多关于您正在尝试执行的操作。
这是一个有趣的话题 – 我可能需要博客
编辑:我现在用两种方法博客 。
我假设你需要一个保存线程的枚举器,所以你应该实现这个。
我只是testing了这一点的代码:
static IEnumerable<int> getNums() { Console.WriteLine("IENUM - ENTER"); for (int i = 0; i < 10; i++) { Console.WriteLine(i); yield return i; } Console.WriteLine("IENUM - EXIT"); } static IEnumerable<int> getNums2() { try { Console.WriteLine("IENUM - ENTER"); for (int i = 0; i < 10; i++) { Console.WriteLine(i); yield return i; } } finally { Console.WriteLine("IENUM - EXIT"); } }
getNums2()总是调用代码的最后部分。 如果你想让你的IEnumerable成为线程安全的,添加你想要的线程锁,而不是书写线,使用ReaderWriterSlimLock,Semaphore,Monitor等
那么,我不知道,但也许在调用者的一些锁?
草案:
Monitor.Enter(syncRoot); foreach (var item in enumerable) { Monitor.Exit(syncRoot); //Do something with item Monitor.Enter(syncRoot); } Monitor.Exit(syncRoot);
我以为你不能让yield
关键字是线程安全的,除非你使它依赖于一个已经线程安全的值来源:
public interface IThreadSafeEnumerator<T> { void Reset(); bool TryMoveNext(out T value); } public class ThreadSafeUIntEnumerator : IThreadSafeEnumerator<uint>, IEnumerable<uint> { readonly object sync = new object(); uint n; #region IThreadSafeEnumerator<uint> Members public void Reset() { lock (sync) { n = 0; } } public bool TryMoveNext(out uint value) { bool success = false; lock (sync) { if (n < 100) { value = n++; success = true; } else { value = uint.MaxValue; } } return success; } #endregion #region IEnumerable<uint> Members public IEnumerator<uint> GetEnumerator() { //Reset(); // depends on what behaviour you want uint value; while (TryMoveNext(out value)) { yield return value; } } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { //Reset(); // depends on what behaviour you want uint value; while (TryMoveNext(out value)) { yield return value; } } #endregion }
您将不得不决定是否每个典型的枚举器启动应重置序列,或者如果客户端代码必须这样做。
你可以每次都返回一个完整的序列,而不是使用yield:
return Enumerable.Range(0, 100).Cast<uint>().ToArray();