2 rust thread problems

  1. rust thread is actually running in a real thread, or is it a virtual thread ?
  2. rust thread.sleep cause main thread sleeped.

my test code is

use std::thread;
use std::time::{Duration};
use chrono::Local;
struct HelloWorld { }

impl HelloWorld {
    fn output(&self) {
        println!("HelloWorld.output! current thread is {:?}. time is {}", thread::current().id(), Local::now().format("%H:%M:%S.%3f"));
        thread::sleep(Duration::from_secs(30));
        println!("after sleeping current thread is {:?}. time is {}", thread::current().id(), Local::now().format("%H:%M:%S.%3f"));
    }

    fn say_hello(&self) {
        thread::scope(|scope| {
           scope.spawn( || {
               self.output();
           });
        });

        println!("after Thread. current thread is {:?}. time is {}", thread::current().id(), Local::now().format("%H:%M:%S.%3f"));
    }
}

fn main() {
    println!("Hello, main! current thread is {:?}, time is {}", thread::current().id(), Local::now().format("%H:%M:%S.%3f"));
    let hw = HelloWorld {};
    hw.say_hello();
    println!("after. thread is {:?}, time is {}", thread::current().id(), Local::now().format("%H:%M:%S.%3f"));
}

click here to view test code runs video.


-------- update code with mio udp server test ---------

Cargo.toml

[dependencies]
chrono = "0.4.44"
mio = {version = "1.2.0", features = ["net", "os-poll", "os-ext"]}

main.rs

use chrono::Local;
use std::thread;
use std::time::Duration;
use mio::{event::Event, net::UdpSocket, Events, Interest, Poll, Token};
use std::{
    io::{self, ErrorKind::WouldBlock},
    str::from_utf8,
};

const SERVER: Token = Token(0);

struct HelloWorld {}

impl HelloWorld {
    fn output(&self) {
        println!(
            "HelloWorld.output! current thread is {:?}. time is {}",
            thread::current().id(),
            Local::now().format("%H:%M:%S.%3f")
        );
        thread::sleep(Duration::from_secs(30));
        println!(
            "after sleeping current thread is {:?}. time is {}",
            thread::current().id(),
            Local::now().format("%H:%M:%S.%3f")
        );
    }

    fn say_hello(&self) {
        thread::scope(|scope| {
            scope.spawn(|| {
                self.output();
            });

            print!("say_hello ???-----");
        });

        println!(
            "after Thread. current thread is {:?}. time is {}",
            thread::current().id(),
            Local::now().format("%H:%M:%S.%3f")
        );
    }

    fn handle(&self, event: &Event, server: &UdpSocket) -> io::Result<()> {
        if event.token() != SERVER {
            return Ok(());
        }

        let mut buffer = vec![0; 4096];
        loop {
            match server.recv_from(&mut buffer) {
                Ok((size, address)) => {
                    println!("客户端: {}", address);
                    let received = &buffer[..size];
                    let str = from_utf8(received).unwrap();
                    println!("收到数据:{}", str);
                    server.send_to(str.to_ascii_uppercase().as_bytes(), address)?;
                }
                Err(e) if e.kind() == WouldBlock => break,
                Err(err) => return Err(err),
            }
        }
        Ok(())
    }

    fn start(&self) -> io::Result<()> {
        let addr = "127.0.0.1:4444".parse().unwrap();
        let mut server = UdpSocket::bind(addr)?;

        let mut poll = Poll::new()?;
        let mut events = Events::with_capacity(128);
        poll.registry()
            .register(&mut server, SERVER, Interest::READABLE)?;

        loop {
            poll.poll(&mut events, None)?; <---- here
            for event in events.iter() {
                self.handle(event, &server)?;
            }
        }
    }

    fn test_start(&self) {
        thread::scope(|scope| {
            scope.spawn(|| {
                let t = self.start();
            });
        });
    }
}

fn main() {
    println!(
        "Hello, main! current thread is {:?}, time is {}",
        thread::current().id(),
        Local::now().format("%H:%M:%S.%3f")
    );
    let hw = HelloWorld {};
    hw.test_start();
    println!(
        "after. thread is {:?}, time is {}",
        thread::current().id(),
        Local::now().format("%H:%M:%S.%3f")
    );
}

run the code , it will be blcoked at
poll.poll(&mut events, None)?;
I want function start runs in a thread. if using scope , main thread should wait , if using thread::spawn , self should be borrowed .

------------------------- update2, -----------------------

use chrono::Local;
use std::thread;
use std::time::Duration;
use mio::{event::Event, net::UdpSocket, Events, Interest, Poll, Token, Waker};
use std::{io::{self, ErrorKind::WouldBlock}, str::from_utf8};
use std::thread::sleep;

const SERVER: Token = Token(0);
const WAKER_TOKEN: Token = Token(1);

struct UdpWrapper {
    socket: UdpSocket,
    events: Events,
    poll: Poll,
    waker: Waker,
}


impl UdpWrapper {

    fn new(ip_address: &str) -> Self {
        let addr = ip_address.parse().unwrap();
        let mut server = UdpSocket::bind(addr).expect("Failed to bind UDP socket");
        let poll = Poll::new().expect("Failed to create Poll");
        let events = Events::with_capacity(128);
        poll.registry().register(&mut server, SERVER, Interest::READABLE).expect("Failed to register socket");

        let waker = Waker::new(poll.registry(), WAKER_TOKEN).expect("Failed to register waker");

        UdpWrapper {
            socket: server,
            events: events,
            poll: poll,
            waker: waker
        }
    }
    fn handle(event: &Event, server: &UdpSocket) -> io::Result<()> {
        if event.token() == WAKER_TOKEN {
            println!("received WAKER_TOKEN single。");
            return Ok(());
        }
        if event.token() != SERVER {
            return Ok(());
        }

        let mut buffer = vec![0; 4096];
        loop {
            match server.recv_from(&mut buffer) {
                Ok((size, address)) => {
                    println!("客户端: {}", address);
                    let received = &buffer[..size];
                    let str = from_utf8(received).unwrap();
                    println!("收到数据:{}", str);
                    server.send_to(str.to_ascii_uppercase().as_bytes(), address)?;
                }
                Err(e) if e.kind() == WouldBlock => break,
                Err(err) => return Err(err),
            }
        }
        Ok(())
    }

    fn start(self) -> io::Result<()> {
        let mut poll = self.poll;
        let mut events = self.events;
        let server = self.socket;

        loop {
            poll.poll(&mut events, None)?;
            for event in events.iter() {
                Self::handle(event, &server).expect("Failed to handle event");
            }
        }
    }

    // fn stop(&mut self){
    fn stop(self){
        let waker = self.waker;
        waker.wake().expect("wake failed");
        println!("-------------- waker.wake() ------------");
    }

    fn test_start(self) {
        thread::spawn(move || {
            self.start().unwrap();
        });
    }
}

fn main() {
    println!(
        "Hello, main! current thread is {:?}, time is {}",
        thread::current().id(),
        Local::now().format("%H:%M:%S.%3f")
    );
    let wrapper = UdpWrapper::new("127.0.0.1:9621");
    wrapper.test_start(); <--- test_start() will take ownership of wrapper, so wrapper.stop() got error.

    sleep(Duration::from_mins(2));
    wrapper.stop(); <-----error here.

    println!(
        "after. thread is {:?}, time is {}",
        thread::current().id(),
        Local::now().format("%H:%M:%S.%3f")
    );
}

Can you cut and paste the output of your program run here rather than expecting us to squint at screen shots or watch videos.

Hello, main! current thread is ThreadId(1), time is 11:08:50.962
HelloWorld.output! current thread is ThreadId(2). time is 11:08:50.963
after sleeping current thread is ThreadId(2). time is 11:09:20.963
after Thread. current thread is ThreadId(1). time is 11:09:20.963
after. thread is ThreadId(1), time is 11:09:20.963

Rust thread is real OS thread. It maps 1 : 1 to OS thread

The main thread is blocked not because your sleep function. But it is the behaviour of std::thread::scoped

std::thread::scoped allows to use local variable with normal reference, without needing to move the variable or static lifetime or smart pointer

Because it uses local data via normal reference, it must guarantee that the thread will not outlive the scope where the data resides. So it will block the main thread until all the threads inside the scope finish

It's use case is when you want to process something parallel to multiple workers, the main thread wait the result to be used for further processing, then the workers return the result to the main thread

If you want to allow the main thread doing something else, you must use the normal thread spawn then calling join() in the place where you want to retrieve the result, eg before the end of the scope before returning value

Rust std::thread threads are OS threads, so it’s as “real” as your OS’s threads are.

std::thread::scope() waits for all the threads spawned using it to terminate before it returns. It must do so or it would be unsound, because the whole point of std::thread::scope() is that all the scoped threads can borrow things from the calling function, which need to continue to be available.

If you want a thread that the caller does not wait for, you must use std::thread::spawn(), not std::thread::scope().

Or, you can do other things in the scope:

fn say_hello(&self) {
    thread::scope(|scope| {
       scope.spawn( || {
           self.output();
       });

       println!("not waiting for thread here. current thread is {:?}. time is {}", thread::current().id(), Local::now().format("%H:%M:%S.%3f"));
    });
}

Thanks for the explanation,it help me.

Thanks, and i have update my code. i want use mio in a thread. but it failed to be achieved. can you give me some suggestion?

Sorry, but that’s way too broad to be answered well. In general, “I want to use X with Y” might introduce the question, but you need to give more detail. For example, you might tell us what you want your program to accomplish, what role mio should play, and also the code of the “failed” program.

Ok, mio poll.poll(&mut events, None) will wait for events, and mio:: Waker can raise a event likes

const WAKER_TOKEN: Token = Token(100);

let waker = Waker::new(poll.registry(), WAKER_TOKEN)?;

waker.wake().expect("unable to wake"); 

use waker.wake() to send single to poll, then poll can capture the single and do something. the waker in main thread not sub-thread ,and the function start in sub-thread.

now, if using thread::spawn in function test_start , there is an error

borrowed data escapes outside of method [E0521]
`self` escapes the method body here

if using thread::scope, sub-thread will be blocked at poll.poll(&mut events, None)?;

what i want is running function start in sub-thread, and can use waker to send a single.

The problem here is that this pattern cannot work:

fn test_start(&self) {
   //         ^ borrowed
   thread::spawn(|| {
       self.start();
       // ^ use of borrow
   });
}

A thread that is to run independently cannot borrow anything; it must own the things it works with. For your example program, this is simple to fix:

fn test_start(self) {
    //        ^ NOT borrowed
    thread::spawn(move || {
        //         ^ transfer ownership to thread
        self.start().unwrap();
    });
}

I uploaded new code "update2", in the code, i renamed HelloWorld to UdpWrapper. and rewritten follow function code

    fn stop(self){
        let waker = self.waker;
        waker.wake().expect("wake failed");
        println!("-------------- waker.wake() ------------");
    }

    fn test_start(self) {
        thread::spawn(move || {
            self.start().unwrap();
        });
    }

after that, got an error

    let wrapper = UdpWrapper::new("127.0.0.1:9621");
    wrapper.test_start();

    sleep(Duration::from_mins(2));
    wrapper.stop(); <----- error here

as you see, wrapper.stop(); wrapper is unavailable.
I don't know hot to do next.

there is only one wrapper.

the wrapper must belong to one thread.
by default only the thread which the wrapper belongs on can use it.

when you do test_start, you send the wrapper to another thread, so it's no longer on the mai thread, so you cannot use it on the main thread.

if you want to share wrapper between mutlitple thread, there are 2 options :

  • references : like you did orginally, you need thread::scope.
    but thread::scope must encapsultate all of the sharing, all the child threads must end before the scope exits, do something like what @kpreid showed above :
  • Arc : this is the simpler, but sometimes more bug-prone, and slightly less optimized version :
    fn run_test(self : Arc<Self>, main_thread_code : impl FnOnce(&Self)) {
        let self_handle = self.clone();
        thread::spawn(move || {
            let t = self_handle.start();
        });
        main_thread_code(&self)
    }

with an Arc, you can have many Arcs on multiple threads that point to the same data

thanks for suggestion.But the function run_test will cause an error at let t = self_handle.start().

cannot move out of an `Arc` [E0507]
move occurs because value has type `UdpWrapper`, which does not implement the `Copy` trait
Note: `UdpWrapper::start` takes ownership of the receiver `self`, which moves value
Help: you can `clone` the value and consume it, but this might not be your desired behavior

UdpWrapper::start needs to take Arc<self>

you mean

    fn start(self: Arc<Self>) -> io::Result<()> {
        let mut poll = self.poll;
        let mut events = self.events;
        let server = self.socket;

        loop {
            poll.poll(&mut events, None)?;
            for event in events.iter() {
                Self::handle(event, &server).expect("Failed to handle event");
            }
        }
    }

if so, there are 3 errors.

let mut poll = self.poll;

cannot move out of an `Arc` [E0507]
move occurs because value has type `mio::Poll`, which does not implement the `Copy` trait
Help: consider borrowing here

let mut events = self.events;

cannot move out of an `Arc` [E0507]
move occurs because value has type `Events`, which does not implement the `Copy` trait
Help: consider borrowing here

let server = self.socket;

cannot move out of an `Arc` [E0507]
move occurs because value has type `mio::net::UdpSocket`, which does not implement the `Copy` trait
Help: consider borrowing here

please show the full code

&mut events

you need unique access to mutate the events
you cannot have both :

  • share self across mutliple threads
  • mutate parts of self

if you want to do shared mutation you wll need some synchronizatin primitive

full code

use chrono::Local;
use std::thread;
use std::time::Duration;
use mio::{event::Event, net::UdpSocket, Events, Interest, Poll, Token, Waker};
use std::{io::{self, ErrorKind::WouldBlock}, str::from_utf8};
use std::cell::Cell;
use std::thread::sleep;
use std::sync:: {Arc, Mutex};

const SERVER: Token = Token(0);
const WAKER_TOKEN: Token = Token(1);

struct UdpWrapper {
    // socket: Cell<UdpSocket>,
    socket: UdpSocket,
    events: Events,
    poll: Poll,
    waker: Waker,
}

impl UdpWrapper {

    fn new(ip_address: &str) -> Self {
        let addr = ip_address.parse().unwrap();
        let mut server = UdpSocket::bind(addr).expect("Failed to bind UDP socket");
        let poll = Poll::new().expect("Failed to create Poll");
        let events = Events::with_capacity(128);
        poll.registry().register(&mut server, SERVER, Interest::READABLE).expect("Failed to register socket");

        let waker = Waker::new(poll.registry(), WAKER_TOKEN).expect("Failed to register waker");

        UdpWrapper {
            socket: server,
            events: events,
            poll: poll,
            waker: waker
        }
    }
    fn handle(event: &Event, server: &UdpSocket) -> io::Result<()> {
        if event.token() == WAKER_TOKEN {
            println!("received WAKER_TOKEN single。");
            return Ok(());
        }
        if event.token() != SERVER {
            return Ok(());
        }

        let mut buffer = vec![0; 4096];
        loop {
            match server.recv_from(&mut buffer) {
                Ok((size, address)) => {
                    println!("客户端: {}", address);
                    let received = &buffer[..size];
                    let str = from_utf8(received).unwrap();
                    println!("收到数据:{}", str);
                    server.send_to(str.to_ascii_uppercase().as_bytes(), address)?;
                }
                Err(e) if e.kind() == WouldBlock => break,
                Err(err) => return Err(err),
            }
        }
        Ok(())
    }

    fn start(self: Arc<Self>) -> io::Result<()> {
        let mut poll = self.poll;
        let mut events = self.events;
        let server = self.socket;

        loop {
            poll.poll(&mut events, None)?;
            for event in events.iter() {
                Self::handle(event, &server).expect("Failed to handle event");
            }
        }
    }

    // fn stop(&mut self){
    fn stop(self){
        let waker = self.waker;
        waker.wake().expect("wake failed");
        println!("-------------- waker.wake() ------------");
    }

    /*
        UdpWrapper 的实例属性于 主线程。
        当调用 test_start 后,UdpWrapper 被转移到 子线程。所以在主线程里无法再访问

    */
    fn test_start(self) {
        let p = Arc::new(self);
        let q = p.clone();
        thread::spawn(move || {
            q.start().unwrap();
        });
    }

    // fn run_test(self : Arc<Self>, main_thread_code : impl FnOnce(&Self)) {
    //     let self_handle = self.clone();
    //     thread::spawn(move || {
    //         let t = self_handle.start();
    //     });
    //     main_thread_code(&self)
    // }
}

fn main() {
    println!(
        "Hello, main! current thread is {:?}, time is {}",
        thread::current().id(),
        Local::now().format("%H:%M:%S.%3f")
    );

    let wrapper = UdpWrapper::new("127.0.0.1:9621");
    wrapper.test_start();

    sleep(Duration::from_mins(2));
    wrapper.stop();

    println!(
        "after. thread is {:?}, time is {}",
        thread::current().id(),
        Local::now().format("%H:%M:%S.%3f")
    );
}

you should really read the book, as @mroth linked.

here is how i would write it :

use chrono::Local;
use std::thread;
use std::time::Duration;
use mio::{event::Event, net::UdpSocket, Events, Interest, Poll, Token, Waker};
use std::{io::{self, ErrorKind::WouldBlock}, str::from_utf8};
use std::cell::Cell;
use std::thread::sleep;
use std::sync:: {Arc, Mutex};
use std::convert::Infallible;

const SERVER: Token = Token(0);
const WAKER_TOKEN: Token = Token(1);

struct UdpWrapper {
    // socket: Cell<UdpSocket>,
    server: UdpSocket,
    events: Events,
    poll: Poll,
}

struct UdpStopper {
    waker: Waker,
    handle : thread::JoinHandle<io::Result<Infallible>>
}



impl UdpWrapper {

    fn new(ip_address: &str) -> Self {
        let addr = ip_address.parse().unwrap();
        let mut server = UdpSocket::bind(addr).expect("Failed to bind UDP socket");
        let poll = Poll::new().expect("Failed to create Poll");
        let events = Events::with_capacity(128);
        poll.registry().register(&mut server, SERVER, Interest::READABLE).expect("Failed to register socket");


        UdpWrapper {
            server,
            events,
            poll,
        }
    }
    fn handle(event: &Event, server: &UdpSocket) -> io::Result<()> {
        if event.token() == WAKER_TOKEN {
            println!("received WAKER_TOKEN single。");
            return Ok(());
        }
        if event.token() != SERVER {
            return Ok(());
        }

        let mut buffer = vec![0; 4096];
        loop {
            match server.recv_from(&mut buffer) {
                Ok((size, address)) => {
                    println!("客户端: {}", address);
                    let received = &buffer[..size];
                    let str = from_utf8(received).unwrap();
                    println!("收到数据:{}", str);
                    server.send_to(str.to_ascii_uppercase().as_bytes(), address)?;
                }
                Err(e) if e.kind() == WouldBlock => break,
                Err(err) => return Err(err),
            }
        }
        Ok(())
    }

    fn start(mut self) -> UdpStopper {
        let waker = Waker::new(self.poll.registry(), WAKER_TOKEN).expect("Failed to register waker");
        let handle = thread::spawn(move || {
            loop {
                self.poll.poll(&mut self.events, None)?;
                for event in self.events.iter() {
                    Self::handle(event, &self.server).expect("Failed to handle event");
                }
            }
        });
        UdpStopper { waker, handle }

    }


    /*
        UdpWrapper 的实例属性于 主线程。
        当调用 test_start 后,UdpWrapper 被转移到 子线程。所以在主线程里无法再访问

    */

    // fn run_test(self : Arc<Self>, main_thread_code : impl FnOnce(&Self)) {
    //     let self_handle = self.clone();
    //     thread::spawn(move || {
    //         let t = self_handle.start();
    //     });
    //     main_thread_code(&self)
    // }
}

impl UdpStopper {
    fn stop(self){
        self.waker.wake().expect("wake failed");
        println!("-------------- waker.wake() ------------");
    }
}

fn main() {
    println!(
        "Hello, main! current thread is {:?}, time is {}",
        thread::current().id(),
        Local::now().format("%H:%M:%S.%3f")
    );

    let wrapper = UdpWrapper::new("127.0.0.1:9621");
    let stopper = wrapper.start();

    sleep(Duration::from_mins(2));
    stopper.stop();

    println!(
        "after. thread is {:?}, time is {}",
        thread::current().id(),
        Local::now().format("%H:%M:%S.%3f")
    );
}

things that belong on the main thread need to be on the main thread.
thing that belong on worker threads need to be on worker threads.
if there is no need to share (i didn't see any need here), there shouldn't be sharing.

i may have misunderstood what you were trying to do though

thanks. as switched from C#, this seems a bit hard to understand.