この POC (概念実証) では、Rust 言語が 競合状態 をどのように扱うかを調査し、C 、広く使用されている言語ですが、競合するためのセキュリティ保証は少なくなります。
Rust Threads の安全性: A と C の比較スレッドの安全性: C から Rust までのデータ競合
索引スレッドは、ソフトウェアタスクを同時に実行できるサブタスクに分割するために使用されます。 スレッドを使用することで、処理時間を獲得し、マシンのリソースをより有効に活用できますが、この競争により、データに深刻な不整合が生じる可能性がある競合状態などの課題が生じます。
スレッド は、タスクを同時に処理できるようにする実行単位です。以下の図に示すように、スレッドはプログラム内の独立した実行フローと考えることができます。
さらに、スレッドを使用して並列処理を実装することもでき、複数のタスクが異なる CPU コアで同時に実行されます。これにより、プログラムは利用可能なハードウェアを有効に活用し、独立したタスクの実行を高速化できます。
3. C での実装
で簡単なシステムを作成しましょう:
初期残高は 1000 です。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; }
を備えた環境を選択すると、いわゆる競合状態が発生する可能性があります。2 つのスレッドが同じ値にアクセスして変更すると、競合状態が発生します。この問題は、呼び出し間の競合により、各スレッドでアクセスされる値の同期が保証されないために発生します。 このコードを複数回実行すると、スレッドが同時にアクセスしてバランスを変更するため、最終的なバランスは変化します。 Mutex は、一度に 1 つのスレッドだけが共有リソースにアクセスできるようにする同期プリミティブです。 mutex の頭字語は、「相互排除」を意味する英語の用語 mutual exclusion に由来しています。 スレッドが ミューテックスを取得すると、同じミューテックスを取得しようとする他のスレッドは、最初のスレッドがミューテックスを解放するまで一時停止されます。これにより、2 つ以上のプロセス (スレッド) が共有リソースに同時にアクセスできなくなります。 Rust を データ競合 のない言語として考えるのは生産的ではありませんが、構造体 とそのコンパイラがメモリとスレッドの安全性のための優れた機能をもたらすことでどのように貢献するのかは理解できます。 Rust は、所有権、借用、同時実行安全構造などの機能を使用して、コンパイル時保証で 競合状態を処理します。 Arc および Mutex 構造体を使用しない Rust では、保護せずに複数の スレッド から 可変 データ (バランス) に直接アクセスすることはできません。 Mutex と Arc を使用して、競合状態 の問題が解決された状態でコードをコンパイルして実行することができました。 Mutex と RwLock は 競合状態 を処理するために使用され、それぞれに特有の利点があります。 Mutex: 1 つのスレッドのリソースへの排他的アクセスを保証し、解放されるまで他のスレッドへのアクセスをブロックします。これはシンプルで効果的ですが、読み取りでもリソースがブロックされるため、読み取りが多いシナリオでは効率が低下します。 RwLock: .read() を使用して複数の同時読み取りを許可し、.write() を使用して排他的書き込みを制限します。 読み取り操作の並列処理が可能になることでパフォーマンスが向上するため、読み取りが優勢なシナリオに最適です C と Rust の比較は、競合状態 を解決するためのさまざまなアプローチを強調しています。 C では競合状態エラーを避けるために注意が必要ですが、Rust では所有権モデルに加えて Mutex、RwLock、Arc などのツールを通じてコンパイル時にこれらのリスクを軽減します。これにより、コードの安全性が高まるだけでなく、サイレントバグを回避することでプログラマーの精神的負担も軽減されます。
競合 システムを開発するための優れた選択肢として自らを位置づけています。
以上がRust Threads の安全性: C との比較。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。
3.2.ミューテックスによる固定
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;
}
4. Rustでの実装
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
}
4.1.競合状態の問題
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.
安全なメカニズムなしで残高が複数のスレッド (handle1 および handle2) に移動されるため、コンパイラーはエラーを生成します。
表示されるエラーメッセージは次のとおりです:
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();
}
4.2. Mutex と Arc による解決
error[E0382]: use of moved value: `saldo`
4.3.ミューテックス vs. Rwロック
5. 結論
6. 参考文献
コード付きリポジトリ: https://github.com/z4nder/rust-data-races