Ich möchte die Synchronisation minimieren und schreiben lock-freie Code wenn möglich in einem Projekt von mir. Wenn es absolut notwendig ist, würde ich gerne leichte Spinlocks ersetzen, die aus atomaren Operationen für pthread und win32 Mutex Locks bestehen. Mein Verständnis ist, dass dies Systemaufrufe unterhalb sind und einen Kontextwechsel verursachen könnten (was für sehr schnelle kritische Abschnitte unnötig sein kann, in denen es einfach wäre, sich ein paar Mal zu drehen).Leichte Spinlocks, die aus atomaren Operationen des GCC gebaut wurden?
Die atomaren Operationen Ich beziehe mich hier gut dokumentiert: http://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Atomic-Builtins.html
Hier ist ein Beispiel zu veranschaulichen, was ich rede. Stellen Sie sich einen RB-Baum mit mehreren Lesern und Schreibern vor. RBTree :: exists() ist schreibgeschützt und Thread-sicher, RBTree :: insert() würde exklusiven Zugriff durch einen einzigen Schreiber (und keine Leser) erfordern, um sicher zu sein. Einige Code:
class IntSetTest
{
private:
unsigned short lock;
RBTree<int>* myset;
public:
// ...
void add_number(int n)
{
// Aquire once locked==false (atomic)
while (__sync_bool_compare_and_swap(&lock, 0, 0xffff) == false);
// Perform a thread-unsafe operation on the set
myset->insert(n);
// Unlock (atomic)
__sync_bool_compare_and_swap(&lock, 0xffff, 0);
}
bool check_number(int n)
{
// Increment once the lock is below 0xffff
u16 savedlock = lock;
while (savedlock == 0xffff || __sync_bool_compare_and_swap(&lock, savedlock, savedlock+1) == false)
savedlock = lock;
// Perform read-only operation
bool exists = tree->exists(n);
// Decrement
savedlock = lock;
while (__sync_bool_compare_and_swap(&lock, savedlock, savedlock-1) == false)
savedlock = lock;
return exists;
}
};
(lässt annehmen, dass es nicht exception-sicher zu sein braucht)
Ist dieser Code in der Tat Thread-sicher? Gibt es irgendwelche Vor-/Nachteile für diese Idee? Irgendein Rat? Ist die Verwendung von Spinlocks so eine schlechte Idee, wenn die Threads nicht wirklich gleichzeitig sind?
Vielen Dank im Voraus. ;)
Die Antwort, die ich in einer ähnlichen Frage gab, http://stackoverflow.com/questions/1919135/critical-sections-that-spin-on-posix/1923218#1923218, wird wahrscheinlich hier relevant sein. –
Ihre Antwort war definitiv relevant für die Verwendung von Spinlocks im Allgemeinen. Sie scheinen eine gute Idee für SMP-Maschinen in ihrem typischen Fall zu sein. Würde die Worst-Case-Situation (ein Schreiber, der während des kritischen Abschnitts nicht mehr läuft) mit dem wahrscheinlicheren Fall, dass zwei gleichzeitige Threads gleichzeitig versuchen einzufügen, ausgeglichen sein? Wie sieht es in einer hybriden Threading-Umgebung aus, in der Benutzer-Threads einer Anzahl von Kernel-Threads zugeordnet sind, die der Anzahl der logischen Prozessoren auf der Maschine entspricht? Die Worst-Case-Situation wäre dann noch weniger wahrscheinlich; Nein? – Thomas
Ich bin mir nicht sicher, inwieweit die Anzahl der Kernel-Threads die Wahrscheinlichkeit beeinflusst, dass Leistungsprobleme auftreten. Es ist möglich, dass der Writer-Thread gerade seinen Zeitschlitz zwischen dem Eingang und dem Ausgang der Sperre verbraucht hat, was zu dem Problemfall führen würde, egal wie viele Kernel-Threads es gibt. An dieser Stelle bemerke ich, dass die RB-Baum-Einfügeoperation O (log (n)) ist. Je größer also der Baum, desto wahrscheinlicher ist dieses Problem. Außerdem verursacht ein größerer Baum wahrscheinlich Seitenfehler während der Aktualisierung, wodurch auch der Problemfall wahrscheinlicher wird. Ich würde Spinlocks hier vermeiden. –