Frage

Ich habe eine generische Erzeuger-Verbraucher-Warteschlange entwickelt, die in der folgenden Art und Weise von Monitor-Impulse:

die enqueue:

    public void EnqueueTask(T task)
    {
        _workerQueue.Enqueue(task);
        Monitor.Pulse(_locker);
    }

die dequeue:

private T Dequeue()
    {
        T dequeueItem;
        if (_workerQueue.Count > 0)
        {
               _workerQueue.TryDequeue(out dequeueItem);
            if(dequeueItem!=null)
                return dequeueItem;
        }
        while (_workerQueue.Count == 0)
            {
                Monitor.Wait(_locker);
        }
         _workerQueue.TryDequeue(out dequeueItem);
        return dequeueItem;
    }

der Warteabschnitt erzeugt die folgende SynchronizationLockException: „-Objekt Synchronisationsverfahren wurde von einem unsynchronisierten Codeblock namens“ brauche ich, um es zu synchronisieren? Warum ? Ist es besser, Verwendung ManualResetEvents oder die Slim-Version von .NET 4.0?

War es hilfreich?

Lösung

Ja, die aktuelle Thread Bedürfnisse „eigenen“ der Monitor, um entweder Wait oder Pulse zu nennen, wie dokumentiert. (So ??werden Sie zum Schloss für Pulse brauchen auch.) Ich weiß nicht, die Details, warum es notwendig ist, aber es ist das gleiche in Java. Ich habe festgestellt, in der Regel würde ich das sowieso obwohl tun will, den anrufende Code sauber zu machen.

Beachten Sie, dass Wait gibt den Monitor selbst, wartet dann auf die Pulse, zurückkauft dann den Monitor vor der Rückkehr.

Wie für die Verwendung von ManualResetEvent oder AutoResetEvent statt -. Sie könnten, aber ich persönlich bevorzuge die Monitor Methoden verwenden, wenn ich einige der anderen Merkmale der Wartegriffe (wie atomar warten auf jede / alle mehrere Griffe) benötigen

Andere Tipps

Aus der MSDN Beschreibung Monitor.Wait ():

Gibt die Sperre für ein Objekt und blockiert den aktuellen Thread, bis er die Sperre zurückkauft.

Die ‚die Sperre‘ Teil das Problem ist, wird das Objekt nicht gesperrt. Sie behandeln die _locker Objekt, als ob es ein Waithandle ist. Ihre eigene Verriegelung Design zu tun, die beweisbar korrekter ist, ist eine Form der schwarzen Magie, die unseren Medizinmann, Jeffrey Richter und Joe Duffy am besten noch übrig ist. Aber ich werde diesen einen Schuß geben:

public class BlockingQueue<T> {
    private Queue<T> queue = new Queue<T>();

    public void Enqueue(T obj) {
        lock (queue) {
            queue.Enqueue(obj);
            Monitor.Pulse(queue);
        }
    }

    public T Dequeue() {
        T obj;
        lock (queue) {
            while (queue.Count == 0) {
                Monitor.Wait(queue);
            }
            obj = queue.Dequeue();
        }
        return obj;
    }
}

In den meisten jedes praktischen Producer / Consumer-Szenario, das Sie an den Hersteller drosseln wollen, damit es nicht in die Warteschlange unbegrenzt füllen kann. Prüfen Duffy BoundedBuffer design für ein Beispiel. Wenn Sie sich leisten können zu .NET bewegen 4.0 dann wollen Sie auf jeden Fall die Vorteile seiner ConcurrentQueue Klasse nehmen, ist es viel mehr schwarze Magie mit geringem Overhead Schließ- und Spin-Warten hat.

Der richtige Weg zur Ansicht Monitor.Wait und Monitor.Pulse / ist PulseAll nicht als Mittel des Wartens Bereitstellung, sondern (für Wait) als ein Mittel, um das System wissen zu lassen, dass der Code in einer Warteschleife, die erst verlassen können etwas von Interesse Veränderungen und (für Pulse / PulseAll) als ein Mittel, um das System wissen zu lassen, dass Code nur etwas verändert hat, dass die Austrittsbedingung erfüllen einige andere Threads Warteschleife verursachen könnten. Man sollte alle Vorkommen von Wait mit Sleep(0) ersetzen können und immer noch Code richtig funktioniert haben (auch wenn viel weniger effizient, als Folge der CPU-Zeit wiederholt die Ausgaben Testbedingungen, die sich nicht verändert haben).

Für diesen Mechanismus zu arbeiten, ist es notwendig, die Möglichkeit der folgenden Sequenz zu vermeiden:

  • Der Code in der Warteschleife testet die Bedingung, wenn es nicht erfüllt ist.

  • Der Code in einem anderen Thread ändert sich der Zustand so, dass sie erfüllt ist.

  • Der Code in dem anderen Thread pulst die Sperre (die noch niemand auf wartet).

  • Der Code in der Warteschleife führt ein Wait seit seiner Bedingung erfüllt wurde nicht.

Die Wait Methode erfordert, dass die Warte Thread eine Sperre haben, da dies der einzige Weg ist, kann es sicher sein, dass die Bedingung, auf sie wartet nicht zwischen der Zeit verändert sie getestet haben und die Zeit, den Code führt die Wait. Die Pulse Verfahren erfordert eine Sperre, weil das der einzige Weg ist, kann es sein, dass Sie sicher, wenn ein anderer Thread „begangen“ selbst eine Wait der Durchführung hat, die Pulse erst erfolgen, nachdem der andere Thread so tatsächlich der Fall ist. Beachten Sie, dass mit Wait innerhalb einer Sperre garantiert nicht, dass es richtig verwendet wird ist, aber es gibt keine Möglichkeit, dass Wait außerhalb eines Schlosses mit möglicherweise richtig sein könnte.

Das Wait / Pulse Design funktioniert eigentlich recht gut, wenn beide Seiten zusammenarbeiten. Die größten Schwächen der Konstruktion, IMHO, sind (1) gibt es keinen Mechanismus für einen Thread, bis eine beliebige Anzahl von Objekten zu warten, wird gepulst; (2), auch wenn man „heruntergefahren“ ein Objekt, so dass alle zukünftigen Warteschleifen sofort verlassen sollte (wahrscheinlich durch eine Exit-Flag überprüft), der einzige Weg, um sicherzustellen, dass Wait, an dem ein Gewinde hat sich verpflichtet, eine Pulse zu bekommen ist die Sperre zu erwerben, möglicherweise auf unbestimmte Zeit für sie zu werden, zur Verfügung zu warten.

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