说我有这个简单的方法:

public IEnumerable<uint> GetNumbers()
{
    uint n = 0;
    while(n < 100)
        yield return n++;
}

你如何使这个线程安全?而且我的意思是你会得到一次枚举器,并且让多个线程处理所有数字,而不会有人重复。

我想在某个地方需要使用一个锁,但是哪个锁必须使迭代器块成为线程安全的?一般来说,如果你想要一个线程安全 IEnumerable&lt; T&gt; ,你需要记住什么?或者说我猜它是一个线程安全的 IEnumerator&lt; T&gt; ...?

有帮助吗?

解决方案

这样做有一个固有的问题,因为 IEnumerator&lt; T&gt; 同时包含 MoveNext() Current 。你真的想要一个电话,如:

bool TryMoveNext(out T value)

此时你可以原子地移动到下一个元素并获得一个值。实现它仍然能够使用 yield 可能会很棘手......但我会考虑一下。我认为你需要包装“非线程安全”。一个线程安全的迭代器,它原子地执行 MoveNext() Current 来实现上面显示的接口。我不知道你怎么把这个界面包装回 IEnumerator&lt; T&gt; ,这样你就可以在 foreach 中使用它了...

如果你正在使用.NET 4.0,那么可以 能够帮助你 - 你需要解释更多关于你想要做什么的事情。

这是一个有趣的话题 - 我可能要写博客......

编辑:我现在用两种方法发表有关它的博客

其他提示

我刚测试了这段代码:

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()始终调用代码的finally部分。如果你希望你的IEnumerable是线程安全的,那么使用ReaderWriterSlimLock,Semaphore,Monitor等添加你想要的任何线程锁而不是writelines。

我假设你需要一个线程保存的枚举器,所以你应该实现那个。

嗯,我不确定,但也许在调用者中有一些锁?

吃水:

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:

返回Enumerable.Range(0,100).Cast&lt; uint&gt;()。ToArray();

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top