In multithreaded programming, locking is a common method to avoid data concurrence. With a lock, the data is protected in a “safe”, ensuring that the data cannot be modified by two threads at the same time, resulting in data corruption. Mutex, read/write, and spin locks are described below.

The mutex

Mutual exclusion means that only one person can obtain and operate resources, while others can obtain the right to use resources after the resources are released. To deepen our understanding, let’s take an example: the resource is like the 21XX book in the library (there is only one book), which is borrowed by student A. The action of borrowing is like “locking”. Student B also went to the library to borrow the book 21XX, but found it was taken away. He could only borrow the book after it was returned. Student A returned the book after reading it. The action of returning it was like “releasing the lock”. Then the book can be borrowed by STUDENT B. The process is as follows:

A borrowed book |... | B borrow books, wait... Reading |... | | A return books... | B borrow booksCopy the code


Corresponding to programming, the process is as follows:

lock()
do somthing
unlock()
Copy the code


Rust instances:

fn main() { 
    let s = std::sync::Arc::new(std::sync::Mutex::new("hello".to_owned()));
    let sc = s.clone();

    letHDL = STD: : thread: : spawn (move | | {/ / acquiring a lock sc. The lock (). Unwrap () push_str (" thread "); // release the lock}); {// get lock s.lock().unwrap().push_str()" main "); } hdl.join().unwrap(); println! ("{:? }", s);
} 
Copy the code

Read-write lock

The purpose of a lock is to ensure that a resource cannot be modified by multiple threads at the same time. Read locks allow multiple threads to read resources at the same time. The read operation itself does not change the state of the resource. Write locks can modify resources, so only one thread is allowed to acquire the write lock. It can be summed up as “multi-reader or single-writer”. Recall the ownership mechanism of Rust, where multiple immutable references or one mutable reference can exist simultaneously to avoid data matchmaking, as with read-write locks.

To deepen the understanding, here’s an example:

  • A, B and C went to the zoo together. They all bought tickets successfully and could enter the zoo together. Buying a ticket acquires a read lock, which is the case with multiple readers.

  • A, B and C went to the zoo together. A successfully bought the ticket and entered the zoo. However, when B bought the ticket, an animal escaped and the zoo needed to be emptied and dealt with. At this moment, D (writer), the master of animal tamer, comes on the stage. There are three people waiting outside the door: B, C and D. D has the highest priority among the three, but needs to wait for A to leave the zoo before entering. The priority of the writer is higher than that of the reader. The priority of the writer is the same among the writers. The writer needs to wait for the reader or other writers to release the lock before he can obtain it.


fn main() {
    let s = std::sync::Arc::new(std::sync::RwLock::new("hello".to_owned()));
    let sc = s.clone();

    let hdl = std::thread::spawn(move || {
        {
            // Get read lock
            println!("{}", sc.read().unwrap());
            // Release the read lock
        }

        // Get the write lock
        sc.write().unwrap().push_str(" thread ");
        // Release the write lock
    });

    {
        // Get the write lock
        s.write().unwrap().push_str(" main ");
        // Release the write lock
    }

    hdl.join().unwrap();
    println!("{:? }", s);
}

Copy the code

spinlocks

A spin lock is similar to a mutex, except that a mutex blocks and waits until the lock is acquired. A spin lock blocks and waits until the lock is acquired. Spin locks are generally used for operations that can be handled quickly, reducing the performance penalty caused by mutex blocking.


// [dependencies]
/ / spin = "0.5"

extern crate spin;

fn main() {
    let s = std::sync::Arc::new(spin::Mutex::new("hello".to_owned()));
    let rs = std::sync::Arc::new(spin::RwLock::new("hello".to_owned()));
    let sc = s.clone();
    let rsc = rs.clone();

    let hdl = std::thread::spawn(move| | {/ / acquiring a lock
        sc.lock().push_str(" thread ");
        rsc.write().push_str(" thread ");
        / / releases the lock
    }); 

    {   
        / / acquiring a lock
        s.lock().push_str(" main ");
        {   
            let st = rs.read();
            println!("{}", *st);
        }   
        rs.write().push_str(" main ");
        / / releases the lock
    }   

    hdl.join().unwrap();

    println!("{:? }", s); 
    println!("{:? }", rs);
}

Copy the code