Rust concurrent programming


Release date:2023-11-10 Update date:2023-11-10 Editor:admin View counts:177

Label:

Rust concurrent programming

Safe and efficient processing of concurrency is one of the purposes of Rust,which mainly solves the problem of high load bearing capacity of the server.

The concept of concurrent refers to the independent execution of different parts of the program, which is easily confused with the concept of parallel,which emphasizes “simultaneous execution”.

Concurrency often leads to parallelism.

This chapter describes programming concepts and details related to concurrency.

Thread

A thread is a part of a program that runs independently.

The difference between a thread and a process (process) is that a thread is a concept within a program, and a program is often executed in a process.

In an environment with an operating system, processes are often scheduled alternately to execute, and threads are scheduled by the program within the process.

As thread concurrency is likely to occur in parallel, deadlocks and delay errors that may be encountered in parallelism often occur in programs with concurrency mechanisms.

In order to solve these problems, many other languages (such as Java, C #) use special runtime software to coordinate resources, but this undoubtedly greatly reduces the efficiency of program execution.

Multi-threading is also supported at the bottom of the operating system, andthe language itself and its compiler do not have the ability to detect and avoid parallel errors, which is very stressful for developers. Developers need to spend a lot of energy to avoid errors.

Rust does not depend on the run-time environment, as is the case with CplinkCure +.

But Rust has designed tools in the language itself, including ownership mechanisms, to eliminate as many common errors as possible in the compilation phase, which is not available in other languages.

But this does not mean that we can be careless when programming, so far the problems caused by concurrency have not been completely solved in the publicdomain, there may still be errors, concurrent programming should be as careful as possible!

Pass through Rust std::thread::spawn function to create a new thread:

Example

use std::thread;
use std::time::Duration;
fn spawn_function() {
    for i in 0..5 {
        println!("spawned thread print {}", i);
        thread::sleep(Duration::from_millis(1));
    }
}
fn main() {
    thread::spawn(spawn_function);
    for i in 0..3 {
        println!("main thread print {}", i);
        thread::sleep(Duration::from_millis(1));
    }
}

Running result:

main thread print 0
spawned thread print 0
main thread print 1
spawned thread print 1
main thread print 2
spawned thread print 2

The order of this result may change in some cases, but in general it is printed like this.

This program has a child thread designed to print five lines of text, and the main thread prints three lines of text, but obviously with the end of the main thread spawn , the thread ends and does not complete all the printing.

std::thread::spawn : The parameter of the function is a no-parameter function, but the above method is not recommended. We can use closures to pass the function as a parameter:

Example

use std::thread;
use std::time::Duration;
fn main() {
    thread::spawn(\|\| {
        for i in 0..5 {
            println!("spawned thread print {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });
    for i in 0..3 {
        println!("main thread print {}", i);
        thread::sleep(Duration::from_millis(1));
    }
}

Closures are anonymous functions that can be saved into variables or passed as arguments to other functions. The closure is equivalent to the Lambda expression in Rust in the following format:

|Parameter 1, Parameter 2,... | -> Return value type{
//Function body
}

For example:

Example

fn main() {
    let inc = \|num: i32\| -> i32 {
        num + 1
    };
    println!("inc(5) = {}", inc(5));
}

Running result:

inc(5) = 6

Closures can omit type declarations using the Rust automatic type determination mechanism:

Example

fn main() {
    let inc = \|num\| {
        num + 1
    };
    println!("inc(5) = {}", inc(5));
}

The result has not changed.

Join method

Example

use std::thread;
use std::time::Duration;
fn main() {
    let handle = thread::spawn(\|\| {
        for i in 0..5 {
            println!("spawned thread print {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });
    for i in 0..3 {
        println!("main thread print {}", i);
        thread::sleep(Duration::from_millis(1));
    }
    handle.join().unwrap();
}

Running result:

main thread print 0
spawned thread print 0
spawned thread print 1
main thread print 1
spawned thread print 2
main thread print 2
spawned thread print 3
spawned thread print 4

join method can cause the child thread to finish running and then stop running the program.

Move forced ownership transfer

This is a common situation:

Example

use std::thread;
fn main() {
    let s = "hello";

    let handle = thread::spawn(\|\| {
        println!("{}", s);
    });
    handle.join().unwrap();
}

It must be wrong to try to use the resources of the current function in the child thread! Because the ownership mechanism prohibits the emergence of such a dangerous situation, it will undermine the certainty of the destruction of resources by the ownership mechanism. We can use closures. move keyword to handle:

Example

use std::thread;
fn main() {
    let s = "hello";

    let handle = thread::spawn(move \|\| {
        println!("{}", s);
    });
    handle.join().unwrap();
}

Message passing

One of the main tools to achieve message passing concurrency in Rust is channel, which consists of two parts, a sender (transmitter) and a receiver (receiver).

std::sync::mpsc Contains methods for message delivery:

Example

use std::thread;
use std::sync::mpsc;
fn main() {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move \|\| {
        let val = String::from("hi");
        tx.send(val).unwrap();
    });
    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}

Running result:

Got: hi

The child thread got the sender of the main thread tx and called its send method sends a string, and the main thread passes through the corresponding receiver rx got it.

Powered by TorCMS (https://github.com/bukun/TorCMS).