In diesem POC (Proof of Concept) werden wir untersuchen, wie die Rust-Sprache Race Conditions behandelt, und sie mit C , eine weit verbreitete Sprache, aber mit weniger Sicherheitsgarantien für den Wettbewerb.
Sicherheit von Rostfäden: Ein Vergleich mit CThreads-Sicherheit: Datenrennen von C nach Rust
IndexThreads verwendet, um Softwareaufgaben in Teilaufgaben zu unterteilen, die gleichzeitig ausgeführt werden können. Durch die Verwendung von Threads gewinnen wir Verarbeitungszeit und nutzen die Ressourcen der Maschine besser aus, aber dieser Wettbewerb bringt Herausforderungen mit sich, wie z. B. Rennbedingungen, die zu schwerwiegenden Inkonsistenzen in den Daten führen können.
Threads sind Ausführungseinheiten, die es Ihnen ermöglichen, Aufgaben gleichzeitig zu bearbeiten. Wir können uns Threads als unabhängige Ausführungsflüsse innerhalb eines Programms vorstellen, wie im Bild unten dargestellt:
Darüber hinaus können Threads zur Implementierung von Parallelität verwendet werden, bei der mehrere Aufgaben gleichzeitig auf verschiedenen CPU-Kernen ausgeführt werden. Dadurch kann das Programm die verfügbare Hardware besser nutzen und die Ausführung unabhängiger Aufgaben beschleunigen.
C erstellen:
int saldo = 1000; void creditar(int valor) { int tmp_saldo = saldo; sleep(1); // Delay simulado saldo += tmp_saldo + valor; } void debitar(int valor) { int temp = saldo; sleep(1); // Delay simulado if (temp >= valor) { saldo = temp - valor; } } void* processar_transacao(void* arg) { int valor = *(int*)arg; if (valor > 0) { creditar(valor); } else { debitar(abs(valor)); } return NULL; } int main() { int transactions[] = {100, -50, 200, -150, 300, -200, 150, -100, 50, -50}; int num_transactions = sizeof(transactions) / sizeof(transactions[0]); pthread_t threads[num_transactions]; for (int i = 0; i < num_transactions; i++) { pthread_create(&threads[i], NULL, processar_transacao, &transactions[i]); // Cria uma thread para cada transação } for (int i = 0; i < num_transactions; i++) { pthread_join(threads[i], NULL); // Aguarda todas as threads terminarem } printf("Saldo final da conta: %d\n", saldo); return 0; }
Multithreading-Verarbeitung entscheiden, können sogenannte Race Conditions auftreten. Wenn zwei Threads auf denselben Wert zugreifen und ihn ändern, liegt eine Race Condition vor. Dieses Problem tritt auf, weil die Synchronisierung des Werts, auf den in jedem Thread zugegriffen wird, aufgrund der Konkurrenz zwischen Aufrufen nicht garantiert ist.
Wenn dieser Code mehrmals ausgeführt wird, variiert der endgültige Saldo, da Threads gleichzeitig auf den Saldo zugreifen und ihn ändern.
int saldo = 1000; void creditar(int valor) { int tmp_saldo = saldo; sleep(1); // Delay simulado saldo += tmp_saldo + valor; } void debitar(int valor) { int temp = saldo; sleep(1); // Delay simulado if (temp >= valor) { saldo = temp - valor; } } void* processar_transacao(void* arg) { int valor = *(int*)arg; if (valor > 0) { creditar(valor); } else { debitar(abs(valor)); } return NULL; } int main() { int transactions[] = {100, -50, 200, -150, 300, -200, 150, -100, 50, -50}; int num_transactions = sizeof(transactions) / sizeof(transactions[0]); pthread_t threads[num_transactions]; for (int i = 0; i < num_transactions; i++) { pthread_create(&threads[i], NULL, processar_transacao, &transactions[i]); // Cria uma thread para cada transação } for (int i = 0; i < num_transactions; i++) { pthread_join(threads[i], NULL); // Aguarda todas as threads terminarem } printf("Saldo final da conta: %d\n", saldo); return 0; }
Mutex ist ein Synchronisationsprimitiv, das sicherstellt, dass jeweils nur ein Thread Zugriff auf eine gemeinsam genutzte Ressource hat. Das Akronym mutex kommt vom englischen Begriff mutual exclusion, was „gegenseitiger Ausschluss“ bedeutet.
Wenn ein Thread einen Mutex erwirbt, wird jeder andere Thread, der versucht, denselben Mutex zu erwerben, angehalten, bis der erste Thread den Mutex freigibt. Dadurch wird verhindert, dass zwei oder mehr Prozesse (Threads) gleichzeitig auf die gemeinsam genutzte Ressource zugreifen.
int saldo = 1000; pthread_mutex_t saldo_mutex; // Mutex para proteger o saldo void creditar(int valor) { pthread_mutex_lock(&saldo_mutex); // Bloqueia o mutex int tmp_saldo = saldo; sleep(1); // Delay simulado saldo = tmp_saldo + valor; pthread_mutex_unlock(&saldo_mutex); // Libera o mutex } void debitar(int valor) { pthread_mutex_lock(&saldo_mutex); // Bloqueia o mutex int tmp_saldo = saldo; sleep(1); // Delay simulado if (tmp_saldo >= valor) { saldo = tmp_saldo - valor; } pthread_mutex_unlock(&saldo_mutex); // Libera o mutex }
Rust als eine Sprache zu betrachten, die im Datenrennen fehlt, ist nicht produktiv, aber wir können verstehen, wie Strukturen und ihr Compiler dazu beitragen, indem sie großartige Funktionen für Speicher- und Thread-Sicherheit bieten.
Rust behandelt Race-Bedingungen mit Garantien zur Kompilierungszeit und verwendet Funktionen wie Ownership, Borrowing und nebenläufigkeitssichere Strukturen:
Ohne die Verwendung von Arc- und Mutex-Strukturen
Rust’s rich type system and ownership model guarantee memory-safety and thread-safety — enabling you to eliminate many classes of bugs at compile-time.
Rust erlaubt keinen direkten Zugriff auf veränderliche Daten (Balance) von mehreren Threads ohne Schutz.
Der Compiler generiert einen Fehler, da der Ausgleich ohne einen sicheren Mechanismus auf mehrere Threads (handle1 und handle2) verschoben wird.
Die angezeigte Fehlermeldung lautet:
fn main() { let mut saldo = 1000; // saldo mutável, mas sem proteção let handle1 = thread::spawn(move || { saldo += 100; // erro: `saldo` é movido para esta thread sem proteção }); let handle2 = thread::spawn(move || { saldo -= 50; // erro: `saldo` é movido para esta thread sem proteção }); handle1.join().unwrap(); handle2.join().unwrap(); }
Mithilfe von Mutex und Arc konnten wir unseren Code kompilieren und ausführen, wobei die Race Condition-Probleme behoben wurden.
error[E0382]: use of moved value: `saldo`
Mutex und RwLock werden verwendet, um Race-Bedingungen zu handhaben, jeweils mit spezifischen Vorteilen:
Mutex: Garantiert den exklusiven Zugriff auf eine Ressource für einen Thread und blockiert den Zugriff für andere, bis er freigegeben wird. Es ist einfach und effektiv, aber selbst Lesevorgänge blockieren die Ressource, was sie in leseintensiven Szenarien weniger effizient macht.
RwLock: Ermöglicht mehrere gleichzeitige Lesevorgänge mit .read() und schränkt das exklusive Schreiben mit .write() ein. Es ist Ideal für Szenarien mit überwiegend Lesevorgängen, da es die Leistung verbessert, indem es Parallelität bei Lesevorgängen ermöglicht.
Der Vergleich zwischen C und Rust zeigt unterschiedliche Ansätze zur Lösung von Race Conditions. Während C Aufmerksamkeit erfordert, um Race-Condition-Fehler zu vermeiden, reduziert Rust diese Risiken zur Kompilierungszeit durch Tools wie Mutex, RwLock und Arc zusätzlich zum Besitzmodell. Dies macht den Code nicht nur sicherer, sondern reduziert auch die mentale Belastung des Programmierers, indem stille Fehler vermieden werden.
Zusammenfassend lässt sich sagen, dass Rust sich als ausgezeichnete Wahl für die Entwicklung konkurrierender Systeme positioniert und Sicherheit und Zuverlässigkeit bietet.
Das obige ist der detaillierte Inhalt vonSicherheit von Rostfäden: Ein Vergleich mit C.. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!