[英]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.