Frage

Sagen, ich habe diese einfache Methode:

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

Wie werden Sie dieses Thema sicher machen? Und damit meine ich, dass Sie das Enumerator einmal bekommen würde, und haben mehrere Threads verarbeiten alle Zahlen, ohne dass jemand immer Duplikate.

Ich nehme eine Sperre verwendet irgendwo werden muss, aber wo muss das Schloss sein für einen Iteratorblock wird Thread-sicher? Was im Allgemeinen, müssen Sie sich merken, wenn Sie einen Thread-sicher IEnumerable<T> wollen? Oder besser gesagt ich denke, es wäre ein Thread-sicher IEnumerator<T> sein ...?

War es hilfreich?

Lösung

Es ist ein inhärentes Problem dabei, weil IEnumerator<T> sowohl MoveNext() und Current hat. Sie wollen wirklich einen einzigen Anruf wie:

bool TryMoveNext(out T value)

an diesem Punkt kann man atomar Umzug in das nächste Element und einen Wert erhalten. Die Implementierung dass und noch in der Lage zu sein yield verwenden könnte schwierig sein ... Ich werde zwar eine Meinung darüber. Ich glaube, Sie müssen das „Nicht-THREAD“ Iterator in einem THREAD eines einzuwickeln, die atomar MoveNext() und Current durchgeführt, um die Schnittstelle zu implementieren oben gezeigt. Ich weiß nicht, wie Sie dann diese Schnittstelle wieder in IEnumerator<T> wickeln würden, so dass Sie es in foreach verwenden könnten aber ...

Wenn Sie .NET 4.0, Parallel Extensions können der Lage sein, mit Ihnen zu helfen, -. Sie bräuchten mehr darüber zu erklären, was Sie versuchen, obwohl zu tun

Dies ist ein interessantes Thema - ich kann über sie haben zum Blog ...

EDIT: Ich habe jetzt darüber gebloggt mit zwei Ansätzen .

Andere Tipps

Ich habe dieses Stück Code getestet:

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 () ruft immer die schließlich Teil des Codes. Wenn Sie Ihr IEnumerable Faden sicher zu sein, hinzufügen, was Thread Sperren Sie statt writelines wollen, verdorren mit ReaderWriterSlimLock, Semaphore, Monitor, etc.

Ich gehe davon aus, dass Sie eine Thread-sichere Enumeration müssen, so sollten Sie vielleicht, dass man implementieren.

Nun, ich bin nicht sicher, aber vielleicht mit einigen Schlössern in den Anrufern?

Entwurf:

Monitor.Enter(syncRoot);
foreach (var item in enumerable)
{
  Monitor.Exit(syncRoot);
  //Do something with item
  Monitor.Enter(syncRoot);
}
Monitor.Exit(syncRoot);

Ich dachte, dass Sie nicht das yield Schlüsselwort Thread-sicher machen, wenn Sie es auf eine bereits Thread-sichere Quelle von Werten abhängig machen:

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
}

Sie werden sich entscheiden müssen, ob jede typische Einleitung eines Aufzählungs sollte die Sequenz zurückgesetzt, oder wenn der Client-Code das tun muss.

Sie könnten nur eine vollständige Sequenz zurückkehren jedes Mal, anstatt Verwendung Ausbeute:

return Enumerable.Range(0, 100).Cast<uint>().ToArray();

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top