0Pricing
Learn Rust Coding · درس

مشاركة الحالة باستخدام Arc/Mutex

بيانات مشتركة آمنة

مشاركة الحالة باستخدام Arc/Mutex درس مجاني في Learn Rust Coding على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Learn Rust Coding، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Learn Rust Coding 4 دروس في المجموع.

بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.

Why Shared State Is Hard

Sometimes message passing is not enough and multiple threads truly need to read and write the same data. Rust will not let you share a mutable value across threads without protection, because that would risk a data race.

The two tools you combine are:

  • Arc for shared ownership across threads.
  • Mutex for safe, exclusive mutation.

Rc Is Not Thread-Safe

Rc gives shared ownership but only on a single thread. Its reference count is not synchronized, so the compiler refuses to send it between threads. For multi-threaded sharing you need Arc (Atomically Reference Counted).

Arc behaves like Rc but updates its count with atomic operations, making clones safe across threads.

use std::sync::Arc;

fn main() {
    let data = Arc::new(vec![1, 2, 3]);
    let clone1 = Arc::clone(&data);
    println!("original: {:?}", data);
    println!("clone:    {:?}", clone1);
    println!("count:    {}", Arc::strong_count(&data));
}

Mutex Provides Exclusive Access

A Mutex wraps data and guarantees only one thread accesses it at a time. You call .lock() to get a MutexGuard, which dereferences to the inner value. The lock is released automatically when the guard goes out of scope.

use std::sync::Mutex;

fn main() {
    let m = Mutex::new(5);
    {
        let mut num = m.lock().unwrap();
        *num += 10;
    } // guard dropped here, lock released
    println!("value = {:?}", m.lock().unwrap());
}

Combining Arc and Mutex

To share mutable data across threads you wrap it as Arc>:

  • Arc lets many threads own a handle to the same data.
  • Mutex lets each thread mutate it safely, one at a time.

Clone the Arc for each thread before spawning.

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let c = Arc::clone(&counter);
    let handle = thread::spawn(move || {
        let mut n = c.lock().unwrap();
        *n += 1;
    });
    handle.join().unwrap();
    println!("counter = {}", *counter.lock().unwrap());
}

A Shared Counter Across Many Threads

The classic example: ten threads each increment a shared counter. Every thread holds its own Arc clone and locks the Mutex to add one. After joining all threads the total is exactly 10, with no data race.

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    for _ in 0..10 {
        let c = Arc::clone(&counter);
        handles.push(thread::spawn(move || {
            let mut num = c.lock().unwrap();
            *num += 1;
        }));
    }
    for h in handles {
        h.join().unwrap();
    }
    println!("Result: {}", *counter.lock().unwrap());
}

Lock Scope Matters

The MutexGuard holds the lock until it is dropped. Holding it across slow work blocks other threads. Keep critical sections short: lock, mutate, release. Wrapping the lock in a small block ensures it drops before any extra processing.

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let log = Arc::new(Mutex::new(Vec::new()));
    let mut handles = vec![];
    for i in 0..3 {
        let l = Arc::clone(&log);
        handles.push(thread::spawn(move || {
            {
                let mut v = l.lock().unwrap();
                v.push(i);
            } // released quickly
        }));
    }
    for h in handles { h.join().unwrap(); }
    let mut result = log.lock().unwrap().clone();
    result.sort();
    println!("{:?}", result);
}

Deadlocks: A Real Risk

A deadlock happens when two threads each hold a lock the other needs, and both wait forever. Rust prevents data races but not deadlocks. Avoid them by always locking multiple mutexes in the same order and keeping locks short.

Also avoid locking the same Mutex twice on one thread; the standard Mutex is not reentrant.

Poisoning When a Thread Panics

If a thread panics while holding a lock, the Mutex becomes poisoned. Later .lock() calls return Err so you know the data may be inconsistent. You can recover the inner guard via into_inner() on the error if you decide the data is still usable.

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let data = Arc::new(Mutex::new(0));
    let d = Arc::clone(&data);
    let _ = thread::spawn(move || {
        let mut g = d.lock().unwrap();
        *g = 7;
        panic!("boom"); // poisons the mutex
    }).join();
    match data.lock() {
        Ok(g) => println!("ok: {}", *g),
        Err(poisoned) => println!("recovered: {}", *poisoned.into_inner()),
    }
}

RwLock for Many Readers

When reads vastly outnumber writes, an RwLock can be faster than a Mutex. It allows many simultaneous readers or one writer. Use .read() for shared access and .write() for exclusive access.

use std::sync::{Arc, RwLock};
use std::thread;

fn main() {
    let config = Arc::new(RwLock::new(String::from("v1")));
    let reader = Arc::clone(&config);
    let r = thread::spawn(move || {
        let val = reader.read().unwrap();
        println!("read: {}", *val);
    });
    r.join().unwrap();
    {
        let mut w = config.write().unwrap();
        *w = String::from("v2");
    }
    println!("final: {}", *config.read().unwrap());
}

Atomics for Simple Counters

For a single integer counter, a full Mutex is overkill. Types like AtomicUsize offer lock-free updates via methods such as fetch_add. Wrap them in Arc to share across threads. Choose an Ordering; SeqCst is the simplest safe default.

use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;

fn main() {
    let counter = Arc::new(AtomicUsize::new(0));
    let mut handles = vec![];
    for _ in 0..5 {
        let c = Arc::clone(&counter);
        handles.push(thread::spawn(move || {
            c.fetch_add(1, Ordering::SeqCst);
        }));
    }
    for h in handles { h.join().unwrap(); }
    println!("count = {}", counter.load(Ordering::SeqCst));
}

Choosing the Right Tool

Quick guidance for shared state:

  • Channel: transfer ownership, pipeline style.
  • Arc<Mutex>: shared mutable structure, mixed read/write.
  • Arc<RwLock>: read-heavy shared data.
  • Atomics: single primitive counters or flags.

Prefer the simplest tool that fits; reach for locks only when message passing does not model the problem well.

Quick Check

Test your understanding of shared state.

Recap

You learned how to share state safely across threads:

  • Arc enables thread-safe shared ownership; Rc does not.
  • Mutex gives exclusive mutation via a guard that auto-unlocks.
  • Arc<Mutex<T>> is the standard pattern for shared mutable data.
  • Keep lock scopes short; beware deadlocks and poisoning.
  • RwLock suits read-heavy data; atomics suit simple counters.

الأسئلة الشائعة

هل درس «مشاركة الحالة باستخدام Arc/Mutex» مجاني؟

نعم — نص درس «مشاركة الحالة باستخدام Arc/Mutex» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Learn Rust Coding، انتقل إلى CoddyKit PRO. تتضمن دورة Learn Rust Coding 4 دروس في المجموع.

ماذا ستتعلم في «مشاركة الحالة باستخدام Arc/Mutex»؟

بيانات مشتركة آمنة تتمرن على Learn Rust Coding مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Learn Rust Coding؟

لا تُشترط خبرة سابقة. Learn Rust Coding على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.

كم من الوقت يستغرق درس «مشاركة الحالة باستخدام Arc/Mutex»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Learn Rust Coding هذا؟

نعم. كل درس في Learn Rust Coding يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. قنوات mpsc
  2. مشاركة الحالة باستخدام Arc/Mutex
  3. الخيوط محددة النطاق
  4. قنوات Crossbeam
← العودة إلى Learn Rust Coding