Mutex and RwLock

Mutex and RwLock are both similar to RefCell in some ways, but not as closely related as Arc is to Rc.

It's Mutex's job to make sure that only one thread has access to the contained data at a time. Since it guarantees that only one block of code has access at all at any given time, a Mutex can safely provide both read and write access without breaking Rust's rules.

In the following example, we have Mutex and Arc in action, and some very basic multithreading:

let counter = Arc::new(Mutex::new(0));

for _ in 0..10 {
let local_counter = Arc::clone(&counter);
thread::spawn(move || {
let wait = time::Duration::new(random::<u64>() % 8, 0);
thread::sleep(wait);
let mut shared = local_counter.lock().unwrap();
*shared += 1;
});
}

loop {
{
let shared = counter.lock().unwrap();
println!("{} threads have completed", *shared);

if *shared >= 10 {
break;
};
};
thread::sleep(time::Duration::new(1, 0));
}

The first thing we're doing is creating a new Arc containing a Mutex, which in turn contains an integer. So, our integer can only be accessed by one thread at a time, but it can be shared among many and its lifetime will not end until all of them are done with it.

Next, we have a for loop, which goes through 10 cycles, and launches a thread on each cycle. Notice how we're creating a clone of the Arc before we call thread::spawn. That's because we're using a closure to define what the threads should do. A closure is like a function in a lot of ways, but it can borrow or move local variables into its own scope when it's defined. We need to create the Arc value that it's going to move into its own scope, before asking it to perform the move.

This closure is moving local variables into its own scope because we used the move keyword when we defined it, and it's moving the local_counter variable specifically simply because we referred to it within the closure.

Within each thread's closure, we ask it to wait for a random duration less than 8 seconds, and then add 1 to the counter. In order to add 1 to the counter, we first have to lock the Mutex, so that we can be sure no other thread has access. We do that by calling the Mutex's lock() function via the Arc (because an Arc can pretend to be a normal borrow of the thing inside it). The value that the lock function returns both provides us with access to the contained data when we dereference it, and keeps track of how long the Mutex should remain locked. When the lifetime of that returned value ends, the Mutex is unlocked so that other threads can access the contained data value. If another thread tries to lock the value while it's still locked, Mutex makes that other thread wait until it's unlocked before continuing.

The lock function actually returns a Result, but we're just unwrapping that here. If the call to lock fails, it's because one of the other threads had a bad error while it had the Mutex locked, and ending the program is probably the smart thing to do.

Finally, we can just do a *shared +=1 to actually add 1 to the shared counter.

After that, we have a loop, which locks the Mutex, then prints out the current value of the counter, and ends the loop (using the break keyword) if it is greater than or equal to 10. If the loop hasn't ended, it then waits one second and does it again.

Notice that within that loop, we have another block expression, and that the thread::sleep call is outside of it. That's because of the way Mutex works: as long as the returned value's lifetime hasn't ended, the Mutex remains locked. We don't want the Mutex to be locked while this code is sleeping, so we put the return value into a shorter scope, so that its lifetime would end before we called thread::sleep, and the Mutex would be unlocked.

An RwLock is similar to a Mutex, but it has different rules about how access to the contained data value is managed. Instead of a single lock function, RwLock has two: read and write. Any number of threads can call read to access the contained information at the same time, but only one thread can use write to access it at any given moment, and while a thread has write access, no other threads are allowed to read it. If a thread tries to read or write at a time when it's not allowed, RwLock makes the thread wait until what it wants to do is allowed again.

We don't need to use read and write together to have both kinds of access. Using write implies that we have read access as well.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.232.11