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