Ich habe eine Situation in C #, wo ich eine Liste von einfachen Typen habe. Auf diese Liste kann von mehreren Threads zugegriffen werden: Einträge können hinzugefügt oder entfernt werden, und das Vorhandensein eines Eintrags kann überprüft werden. Ich habe die Liste in ein Objekt eingekapselt, das gerade diese drei Operationen bisher gezeigt hat.
Ich habe ein paar Fälle zu behandeln (nicht genau die gleichen wie die Methoden, die ich gerade erwähnt habe).
1. Ein Thread kann nur nach dem Vorhandensein eines Eintrags suchen. (einfach)
2. Ein Thread kann nach dem Vorhandensein eines Eintrags suchen, und falls dieser nicht existiert, fügen Sie ihn hinzu.
3. Ein Thread muss prüfen, ob ein Eintrag vorhanden ist, und falls dies der Fall ist, warten, bis er entfernt wurde.
4. Eine Kombination von 2 und 3, bei der ein Thread nach dem Vorhandensein eines Eintrags sucht, falls dieser existiert, muss er warten, bis er entfernt wird, bevor er ihn selbst hinzufügen kann.
Die ganze Idee ist, dass die Existenz eines Eintrags eine Sperre bedeutet. Wenn ein Eintrag vorhanden ist, kann das Objekt, das er identifiziert, nicht geändert werden, und der Code kann nicht fortgesetzt werden, da er an anderer Stelle geändert wird.
Das mag wie eine einfache Anfänger-Situation erscheinen, aber ich rege mich bei Nebenläufigkeitsproblemen auf und es macht mich ein bisschen paranoid, und ich bin auch nicht so vertraut mit den Nebenläufigkeitsmechanismen von C #.
Was wäre der beste Weg, damit umzugehen? Bin ich total ausgeschaltet? Sollte check und add (test und set?) Zu einer vierten atomaren Operation kombiniert werden? Würde ich meinen Methoden, auf die die Liste zugegriffen wird, einfach Sperrblöcke hinzufügen?
Ist es auch möglich, Einheiten dieser Art zu testen (nicht die einfachen Operationen, die Nebenläufigkeitssituationen)?
Unit Testing wird sicherlich schwer sein.
Dies kann alles vernünftig einfach mit den "nativen" Gleichzeitigkeitsmechanismen in .NET erfolgen: lock-Anweisungen und Monitor.Wait
/ Monitor.PulseAll
. Sofern Sie nicht über einen separaten Monitor pro Element verfügen, müssen Sie alle Threads aufwecken, sobald etwas entfernt wird - andernfalls können Sie dem "richtigen" Thread nicht mehr sagen, dass er aufwachen soll.
Wenn es wirklich nur ein Satz von Elementen ist, können Sie HashSet<T>
anstelle von List<T>
verwenden, um die Sammlung zu repräsentieren, übrigens - nichts, was Sie erwähnt haben, ist zu tun mit der Bestellung.
Beispielcode unter der Annahme, dass ein Satz für Sie in Ordnung ist:
%Vor%(zugegebenermaßen völlig ungeprüft. Vielleicht möchten Sie auch Timeouts für den wartenden Code angeben ...)
Obwohl # 1 am einfachsten zu schreiben ist, ist es im Grunde eine nutzlose Methode. Wenn Sie nicht nach Abschluss einer Abfrage für "Vorhandensein eines Eintrags" dieselbe Sperre beibehalten, geben Sie tatsächlich "Existenz eines Eintrags zu einem bestimmten Zeitpunkt in der Vergangenheit" zurück. Es gibt Ihnen keine Informationen über die aktuelle Existenz des Eintrags.
Wenn zwischen der Erkennung eines Wertes in der Liste und dem Ausführen einer Operation zum Abrufen der Wert entfernt wurde, könnte ein anderer Thread kommen und ihn entfernen.
Enthält Operationen auf einer gleichzeitigen Liste sollten mit der Operation kombiniert werden, die Sie im Falle einer echten / falschen Existenz dieser Prüfung durchführen möchten. Zum Beispiel ist TestAdd () oder TestRemove () viel sicherer als Enthält + Hinzufügen oder Enthält + Entfernen
Hier ist eine ordnungsgemäße parallele thread-sichere parallelisierbare Liste-Implementierung Ссылка
Es gibt ein Produkt zum Finden von Rennbedingungen und dergleichen in Einzeltests. Es heißt TypeMock Racer . Ich kann jedoch nichts dafür oder dagegen sagen. :)
Tags und Links c# concurrency