Rust Atomics und Locks Lesenotizen Kapitel 1 Grundlagen der Rust-Parallelität

Informationen zum Originalbuch von Rust Atomic and Locks

Autor: Mara Bos
Verlag: O'REILLY
ISBN: 978-1-098-11944-7

Kapitel 1 Grundlagen der Rust-Parallelität

  1. Vor dem Aufkommen von Mehrkernprozessoren erreichte das Betriebssystem Parallelität durch schnelles Umschalten zwischen Prozessen, und jeder Prozess lief eine Zeit lang; nach dem Aufkommen von Mehrkernprozessoren wurde echte Parallelität (Parallelität) realisiert;
  2. Verschiedene Prozesse sind voneinander isoliert und können nur über den Betriebssystemkern auf den Speicher des anderen zugreifen. Der Prozess kann jedoch Threads (Threads) generieren (spawnen), und die Threads teilen sich den Speicherplatz, sodass sie problemlos miteinander kommunizieren können.
  3. In Rust std::thread::spawnwird über die Standardbibliothek ein neuer Thread generiert. Für diese Methode ist ein Parameter erforderlich. Der Parameter ist die Funktion (Funktion), die der Thread zum Ausführen verwendet. Sie können ihn verwenden, um die ID des aktuellen Threads abzurufen. Diese ID kann nur zum Vergleichen und Kopieren verwendet werden, und es gibt keine Garantie dafür, dass die generierten IDs kontinuierlich sind thread::current()::id().
  4. let t = thread::spawn(f)Wenn die Hauptfunktion beendet wird, wird das gesamte Programm beendet. Selbst wenn andere erzeugte Threads noch ausgeführt werden, müssen Sie den Rückgabewert (einen JoinHandle-Typ) recyceln, wenn Sie die Hauptfunktion verlassen müssen, nachdem alle Threads abgeschlossen sind t.join().unwrap().
  5. ! println!Das Makro verwendet std::io::Stdout::lock()eine Sperre, um sicherzustellen, dass jeder Ausdruck nicht durch Ausdrucke anderer Threads beeinträchtigt wird.
  6. spawnFunktionen haben eine 'staticstatische Lebenszyklusbeschränkung, die erfordert, dass die erzeugte Funktion überlebt, wenn das gesamte Programm ausgeführt wird. fn function()Es gibt kein Problem mit der normal deklarierten Funktion, aber wenn der Abschluss eine lokale Variable durch Referenz erfasst (Erfassung durch Referenz), bleibt der Abschluss möglicherweise nicht dauerhaft bestehen, da die durch den Abschluss erfasste Referenz ebenfalls ungültig wird, wenn die lokale Variable zerstört wird. Einzelheiten finden Sie auf Seite 4 des Originalbuchs.
  7. std::thread::spawnEs handelt sich eigentlich um std::thread::Builder::new().spawn().unwrap()eine Abkürzung, mit der die generierten Threads angepasst werden können std::thread::Builder, z. B. um Stapelspeicherplatz zuzuweisen und dem Thread einen Namen zu geben ( std::thread::current().name());
  8. Wenn Sie einen Thread erzeugen möchten, um lokale Variablen auszuleihen und gleichzeitig die Prüfung des Deklarationszyklus vermeiden möchten, können Sie std::thread::scopeeinen Thread mit einem begrenzten Gültigkeitsbereich bis deklarieren. Solange festgestellt wird, dass der Zeitraum des Gültigkeitsbereichs kürzer ist als der Lebenszyklus der lokalen Variablen, können Sie die Spawn-Funktion innerhalb des Gültigkeitsbereichs sicher verwenden, um lokale Variablen auszuleihen. Einzelheiten finden Sie auf Seite 5.
  9. Statische Variablen werden durch staticSchlüsselwörter deklariert. Der Lebenszyklus der Variablen ist das gesamte Programm. Andere Threads können auf die Variable zugreifen, können die Variable jedoch nicht besitzen, da sie dem gesamten Programm gehört (eigener);
  10. Durch Box::leakdie Freigabe des Besitzes des Zeigers wird garantiert, dass er niemals gelöscht wird, sodass der Zeiger einen statischen Lebenszyklus hat und von allen Threads ausgeliehen werden kann.
  11. 'staticDer Lebenszyklus bedeutet nicht unbedingt, dass der Wert zu Beginn des Programms vorhanden ist, sondern dass er bis zum Ende des Programms bestehen bleiben muss.
  12. std::rc::RcÄhnlich wie Box, aber mit gezählter Referenz, cloneist das Ergebnis derselbe Zeiger, der sich vom Box-Klon unterscheidet; Rc ist nicht threadsicher und kann daher nicht sendin anderen Threads verwendet werden Arc, es ist Rceine threadsichere Version;
  13. Rust erlaubt und empfiehlt, neue Referenzvariablen zu definieren und gleichzeitig den Variablennamen der alten Variablen, sogenannte Schattenvariable, zu verwenden, was durch die Erstellung eines neuen Bereichs (Scope) realisiert wird, siehe Seite 10 für Details;
  14. Unveränderliches Ausleihen (&T) kann kopiert werden, und veränderliches Ausleihen stellt sicher, dass es sich um das einzige aktive Ausleihen von Daten handelt.
  15. unsafeEs ist unmöglich, unsicheren Code zu schreiben, der die Sicherheitsprüfungen des Rust-Compilers besteht, es sei denn, Sie verwenden ;
  16. Die interne Veränderbarkeit ermöglicht nur Mutationen bei gemeinsamer Ausleihe. Wenn es sich um eine exklusive Ausleihe handelt, kann dennoch garantiert werden, dass keine andere Ausleihe vorhanden ist. Natürlich ist unsicherer Code eine Ausnahme, da dadurch mehrere veränderbare Ausleihen gleichzeitig vorhanden sein können.
  17. std::cell::Cell<T>Umschließt nur einen T-Typ, erlaubt interne Variablen bei gemeinsamer Ausleihe, ermöglicht Benutzern jedoch nur das Herauskopieren (herauskopieren) oder Herausnehmen () und Ersetzen als Ganzes und kann nur in einem einzelnen Thread verwendet werden. Unterstützt kein Multithreading .take().
  18. std::cell::RefCell::<T>Der Unterschied zu Cell besteht darin, dass RefCell .borrow()den Inhalt von T ausleihen kann ( ), was einen geringen Laufzeitaufwand verursacht. Gleichzeitig kann RefCell alle Ausleihen verfolgen. Wenn T bereits von Variablen ausgeliehen wurde ( ), gerät das Programm in Panik und umgekehrt; wie Cell kann RefCell nur in einem einzelnen Thread verwendet werden .borrow_mut();
  19. MutexUnd RwLockist die gleichzeitige Version von RefCell, was bedeutet, dass sie die Verwendung mit mehreren Threads unterstützen. Der Unterschied zu RefCell besteht darin, dass RefCell erkennt, dass sich gegenseitig ausschließende Ausleihen (z. B. gleichzeitiges veränderliches und unveränderliches Ausleihen) in Panik geraten, während Mutex und RwLock warten (oder schlafen) und mit dem nächsten Schritt fortfahren, bis die gegenseitige Ausschlussbedingung berührt wird. RwLock () ermöglicht veränderliches und unveränderliches Ausleihen, während Mutex nur eindeutiges Ausleihen unterstützt.
  20. AtomicDer Typ ist Celleine gleichzeitige Version, aber im Gegensatz zu Cell ist eine Vorlagenklasse. Atomic hat keinen Atomic<T>Typ, sondern wird beispielsweise einzeln für integrierte Typen implementiert AtomicBool.
  21. UnsafeCellEs ist die Quellsprache (primitiv) der internen Variabilität. Typen, die sichere interne Variabilität bereitstellen, wie z. B. und, werden alle durch UnsafeCell implementiert, das einen Rohzeiger in UnsafeCell einschließt Cell;Mutex
  22. Ein Typ kann Sendangeben, dass sein Besitz auf andere Threads übertragen werden kann, und ein Typ kann Syncangeben, dass seine gemeinsame Referenz (gemeinsame Referenz, &T) auf andere Threads übertragen werden kann (d. h. senden);
  23. Alle integrierten Typen wie i32, bool und str sind Sendsum Sync. Wenn die Mitglieder eines neuen Typs Sendsum implementieren Sync, implementiert dieser Typ auch automatisch diese beiden Merkmale. Wenn Sie diese beiden automatisch implementierten Merkmale aufheben möchten, können Sie std::marker::PhantomData<T>der Struktur eine Mitgliedsvariable hinzufügen, die Send und Sync nicht implementiert. Cell<T>Weitere Informationen finden Sie beispielsweise auf Seite 16.
  24. Nackte Zeiger (*const T, &mut T) werden nicht implementiert Send. SyncWenn Sie diese beiden Merkmale implementieren müssen, müssen Sie das Schlüsselwort unsafe verwenden.
  25. Mutex<T>Verwenden Sie lock()die Methode, um die Sperre abzurufen, einen MutexGuardTyp abzurufen, sicherzustellen, dass die Sperre erhalten wurde, um den Inhalt, den sie schützt, eindeutig zu ändern, und verwenden Sie Drop()die Methode, um sie zu entsperren. into_inner()Die Methode entzieht dem Mutex den Besitz, sodass kein Thread mehr darauf verweist und der Sperrvorgang nicht mehr ausgeführt werden kann.
  26. Wenn ein Thread, der die Sperre erworben hat, in Panik gerät, gibt er die Sperre frei. Wenn jedoch andere Threads versuchen, die Sperre .lock()zu PoisonErrorerwerben, erhält er eine. Dies zeigt an, dass der Mutex vergiftet (vergiftet) wurde und der vergiftete Zustand an alle Benutzer weitergegeben werden kann, die versuchen, die Sperre zu erwerben.
  27. ! Es kann praktisch sein, ohne Bindung von Variablen für .lock()die zurückgegebenen Ergebnisse zu schreiben let, aber es ist leicht, in einige Fallen zu tappen, die nicht leicht zu erkennen sind. Wenn Sie die match, if let, while letWait-Anweisung verwenden, um das Gleichheitszeichen rvalue zu binden lock(), wird die Sperre gehalten, {code block}bis alle Ausführungen abgeschlossen sind, nicht if letbis die Anweisung endet. Einzelheiten finden Sie auf Seite 21;
  28. Mutex<T>和RwLock<T>Beide erfordern die Implementierung von T Send, aber RwLock erfordert zusätzlich die Implementierung von T Sync;
  29. Im Gegensatz zum Mutex in C oder C++ enthält der Mutex von Rust die zu schützenden Daten. Wenn Sie lediglich eine Sperre benötigen, können Sie einen Typ der Größe Null verwenden oder definieren, um Mutex<()>den kritischen Abschnitt zu schützen;
  30. Pass zum Anhalten eines Threads und Pass zum Aufwecken des Threads, std::thread::park()wenn bestimmte Bedingungen erfüllt sind . Beispiele finden Sie auf Seite 24;unpark()
  31. ! Wenn ein Thread zuvor park()eine Nachricht empfangen hat unpark(), geht die Nachricht nicht verloren, sondern wird aufgezeichnet. Wenn er die Nachricht das nächste Mal empfängt, park()wird er schnell unpark () ausführen, um den folgenden Code auszuführen. Er wird jedoch nur unpark()einmal aufgezeichnet, und der Thread ohne park () zeichnet nach dem Empfang von 2 unpark () nur einmal auf. Der Park ()-Mechanismus eignet sich besser für Szenarien mit 1 Verbraucher und 1 Hersteller.
  32. Bedingungsvariablen std::sync::Condvarkönnen verwendet werden, um mit mehreren Verbrauchern umzugehen, die benachrichtigt werden müssen, notify_onezum Aufwecken eines wartenden Threads und notify_allzum Aufwecken aller Threads, die auf eine bestimmte Bedingung warten. Beachten Sie, dass wait()die Methode Unlocking, waiting, relockingOperationen in derselben Funktion integriert und garantiert eine atomare Operation ist. Beispiele finden Sie auf Seite 27, normalerweise Condvarnur mit einem einzigen Mutex;

おすすめ

転載: blog.csdn.net/Mint2yx4/article/details/130522222