簡體   English   中英

如何將 function 作為線程參數傳遞?

[英]How to pass a function as parameter for thread?

我是一個新的 rustacean,我嘗試將 function 作為參數傳遞到另一個 function 中,以便創建具有 function 傳遞作為參數的線程。

這里的代碼:

use std::os::unix::net::{UnixListener, UnixStream};
use std::thread;
use std::io::Read;
use anyhow::Context;

pub struct SocketServer {
    path: String,
    listener: UnixListener,
}

impl SocketServer {
    pub fn new(path: &str) -> anyhow::Result<SocketServer>{

        if std::fs::metadata(path).is_ok() {
            println!("A socket is already present. Deleting...");
            std::fs::remove_file(path).with_context(|| {
                format!("could not delete previous socket at {:?}", path)
            })?;
        }

        let socket_listener =
            UnixListener::bind(path).context("Could not create the unix socket")?;

        let path = path.to_string();

        Ok(SocketServer{ path, listener: socket_listener })
    }

    pub fn start(&self, f: &dyn Fn(UnixStream)) -> anyhow::Result<()>{

        for stream in self.listener.incoming() {
            match stream {
                Ok(stream) => {
                    thread::spawn(||f(stream));
                }
                Err(err) => {break;}
            }
        }
        Ok(())
    }
}

pub fn handle_stream(mut unix_stream: UnixStream) -> anyhow::Result<()> {
    let mut message = String::new();
    unix_stream
        .read_to_string(&mut message)
        .context("Failed at reading the unix stream")?;

    println!("We received this message: {}", message);

    Ok(())
}

這是我在嘗試編譯時遇到的錯誤:

error[E0277]: `dyn Fn(UnixStream)` cannot be shared between threads safely
   --> src/socket.rs:34:35
    |
34  |                     thread::spawn(||f(stream));
    |                     ------------- ^^^^^^^^^^^ `dyn Fn(UnixStream)` cannot be shared between threads safely
    |                     |
    |                     required by a bound introduced by this call
    |
    = help: the trait `Sync` is not implemented for `dyn Fn(UnixStream)`
    = note: required for `&dyn Fn(UnixStream)` to implement `Send`

我在Rust這本書中得到了一些信息,但我仍然不明白哪個function需要實現什么。 你能給我一些提示嗎? (其他部分也歡迎指教)

我試圖刪除閉包,但它會出現另一個錯誤:

error[E0277]: expected a `FnOnce<()>` closure, found `()`
   --> src/socket.rs:34:35
    |
34  |                     thread::spawn(f(stream));
    |                     ------------- ^^^^^^^^^ expected an `FnOnce<()>` closure, found `()`
    |                     |
    |                     required by a bound introduced by this call
    |
    = help: the trait `FnOnce<()>` is not implemented for `()`
    = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }`
note: required by a bound in `spawn`
   --> /rust/lib/rustlib/src/rust/library/std/src/thread/mod.rs:661:8
    |
661 |     F: FnOnce() -> T,
    |        ^^^^^^^^^^^^^ required by this bound in `spawn`

dyn Fn()可能會捕獲在線程之間共享不安全的東西(例如Rc s)。 所以你需要將其標記為Sync

pub fn start(&self, f: &(dyn Fn(UnixStream) + Sync)) -> anyhow::Result<()> {

現在你會得到另一個錯誤:

error[E0521]: borrowed data escapes outside of associated function
  --> src/lib.rs:34:21
   |
30 |     pub fn start(&self, f: &(dyn Fn(UnixStream) + Sync)) -> anyhow::Result<()> {
   |                         -  - let's call the lifetime of this reference `'1`
   |                         |
   |                         `f` is a reference that is only valid in the associated function body
...
34 |                     thread::spawn(|| f(stream));
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                     |
   |                     `f` escapes the associated function body here
   |                     argument requires that `'1` must outlive `'static`

因為函數捕獲的數據可能在線程退出前就被丟棄,造成use after free。 最簡單的解決方案是使用Arc

pub fn start(&self, f: Arc<dyn Fn(UnixStream) + Sync + Send>) -> anyhow::Result<()> {
    for stream in self.listener.incoming() {
        match stream {
            Ok(stream) => {
                let f = Arc::clone(&f);
                thread::spawn(move || f(stream));
            }
            Err(err) => {
                break;
            }
        }
    }
    Ok(())
}

(您還需要Send綁定)。

你有理由需要dyn Fn嗎? 最簡單的可能是接受impl Fn(UnixStream) + Send + Clone + 'static

pub fn start<F>(&self, f: F) -> anyhow::Result<()>
where
    F: Fn(UnixStream) + Send + Clone + 'static,
{
    for stream in self.listener.incoming() {
        let f = f.clone();
        match stream {
            Ok(stream) => {
                thread::spawn(move || f(stream));
            }
            Err(err) => break,
        }
    }
    Ok(())
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM