Ich habe mit Multithreading und Parallelverarbeitung experimentiert und brauchte einen Zähler, um eine grundlegende Zählung und statistische Analyse der Verarbeitungsgeschwindigkeit durchzuführen. Um Probleme bei der gleichzeitigen Verwendung meiner Klasse zu vermeiden, habe ich eine Sperranweisung für eine private Variable in meiner Klasse verwendet:
private object mutex = new object();
public void Count(int amount)
{
lock(mutex)
{
done += amount;
}
}
Aber ich habe mich gefragt ... wie teuer ist das Sperren einer Variablen? Was sind die negativen Auswirkungen auf die Leistung?
c#
.net
multithreading
locking
parallel-processing
Kees C. Bakker
quelle
quelle
Antworten:
Hier ist ein Artikel , der in die Kosten geht. Kurze Antwort ist 50ns.
quelle
lock
mehr als Sie über die Kosten der Verwendung einer variablen sorgen würde.Die technische Antwort lautet, dass dies nicht quantifizierbar ist. Dies hängt stark vom Status der Rückschreibpuffer des CPU-Speichers ab und davon, wie viele Daten, die der Prefetcher gesammelt hat, verworfen und erneut gelesen werden müssen. Welche sind beide sehr nicht deterministisch. Ich verwende 150 CPU-Zyklen als Back-of-the-Envelope-Näherung, um größere Enttäuschungen zu vermeiden.
Die praktische Antwort ist, dass es viel billiger ist als die Zeit, die Sie beim Debuggen Ihres Codes benötigen, wenn Sie glauben, eine Sperre überspringen zu können.
Um eine harte Zahl zu erhalten, müssen Sie messen. Visual Studio verfügt über einen Slick Concurrency Analyzer als Erweiterung.
quelle
Weiterführende Literatur:
Ich möchte einige meiner Artikel vorstellen, die sich für allgemeine Synchronisationsprimitive interessieren und sich mit Monitor, Verhalten, Eigenschaften und Kosten von C # -Sperranweisungen befassen, abhängig von bestimmten Szenarien und der Anzahl der Threads. Es ist speziell an CPU-Verschwendung und Durchsatzzeiten interessiert, um zu verstehen, wie viel Arbeit in mehreren Szenarien durchgesetzt werden kann:
https://www.codeproject.com/Articles/1236238/Unified-Concurrency-I-Introduction https://www.codeproject.com/Articles/1237518/Unified-Concurrency-II-benchmarking-methodologies https: // www. codeproject.com/Articles/1242156/Unified-Concurrency-III-cross-benchmarking
Ursprüngliche Antwort:
Ach je!
Es scheint, dass die richtige Antwort, die hier als DIE ANTWORT gekennzeichnet ist, von Natur aus falsch ist! Ich möchte den Autor der Antwort respektvoll bitten, den verlinkten Artikel bis zum Ende zu lesen. Artikel
Der Autor des Artikels von 2003 Artikeln wurde die Messung auf Dual - Core - Maschine nur und in dem ersten Mess Fall er gemessen mit einer einzigen Gewindesicherung nur und das Ergebnis war etwa 50 ns pro Schloss Zugang.
Es sagt nichts über eine Sperre in der gleichzeitigen Umgebung aus. Wir müssen also den Artikel weiter lesen und in der zweiten Hälfte hat der Autor das Sperrszenario mit zwei und drei Threads gemessen, was den Parallelitätsstufen der heutigen Prozessoren näher kommt.
Der Autor sagt also, dass bei zwei Threads auf Dual Core die Sperren 120 ns kosten und bei drei Threads 180 ns. Es scheint also eindeutig von der Anzahl der Threads abhängig zu sein, die gleichzeitig auf die Sperre zugreifen.
Es ist also einfach, es sind keine 50 ns, es sei denn, es ist ein einzelner Thread, bei dem die Sperre unbrauchbar wird.
Ein weiteres zu berücksichtigendes Problem ist, dass es als durchschnittliche Zeit gemessen wird !
Wenn die Zeit der Iterationen gemessen würde, gäbe es sogar Zeiten zwischen 1 ms und 20 ms, einfach weil die Mehrheit schnell war, aber nur wenige Threads auf die Prozessorzeit warten und sogar Millisekunden lange Verzögerungen verursachen.
Dies sind schlechte Nachrichten für jede Art von Anwendung, die einen hohen Durchsatz und eine geringe Latenz erfordert.
Und das letzte zu berücksichtigende Problem ist, dass es innerhalb des Schlosses zu langsameren Vorgängen kommen kann, und dies ist sehr oft der Fall. Je länger der Codeblock innerhalb des Schlosses ausgeführt wird, desto höher ist die Konkurrenz und die Verzögerungen steigen himmelhoch.
Bitte beachten Sie, dass bereits seit 2003 mehr als ein Jahrzehnt vergangen ist, dh nur wenige Generationen von Prozessoren, die speziell für den vollständigen gleichzeitigen Betrieb entwickelt wurden und das Sperren ihre Leistung erheblich beeinträchtigt.
quelle
Dies beantwortet Ihre Frage zur Leistung nicht, aber ich kann sagen, dass .NET Framework eine
Interlocked.Add
Methode bietet , mit der Sie Ihreamount
zu Ihremdone
Mitglied hinzufügen können , ohne ein anderes Objekt manuell zu sperren.quelle
lock
(Monitor.Enter / Exit) ist sehr billig, billiger als Alternativen wie Waithandle oder Mutex.Aber was wäre, wenn es (ein wenig) langsam wäre, hätten Sie lieber ein schnelles Programm mit falschen Ergebnissen?
quelle
Die Kosten für ein Schloss in einer engen Schleife sind im Vergleich zu einer Alternative ohne Schloss enorm. Sie können es sich leisten, viele Schleifen zu erstellen und trotzdem effizienter als ein Schloss zu sein. Deshalb sind sperrfreie Warteschlangen so effizient.
Ausgabe:
quelle
Es gibt verschiedene Möglichkeiten, "Kosten" zu definieren. Es gibt den tatsächlichen Aufwand für das Erhalten und Freigeben des Schlosses; Wie Jake schreibt, ist das vernachlässigbar, es sei denn, diese Operation wird millionenfach ausgeführt.
Von größerer Bedeutung ist die Auswirkung, die dies auf den Ausführungsfluss hat. Dieser Code kann jeweils nur von einem Thread eingegeben werden. Wenn Sie 5 Threads haben, die diesen Vorgang regelmäßig ausführen, warten 4 von ihnen darauf, dass die Sperre freigegeben wird, und sind dann der erste Thread, der nach der Freigabe dieser Sperre diesen Code eingibt. Ihr Algorithmus wird also erheblich leiden. Wie viel davon abhängt, hängt vom Algorithmus ab und davon, wie oft die Operation aufgerufen wird. Sie können es nicht wirklich vermeiden, ohne die Rennbedingungen einzuführen, aber Sie können es verbessern, indem Sie die Anzahl der Aufrufe des gesperrten Codes minimieren.
quelle