rust 如何 Package std::io::写入互斥体以跟踪订阅者?

ffx8fchx  于 2023-02-04  发布在  其他
关注(0)|答案(2)|浏览(151)

Mutex的文档中,它说它实现了SendSync--这是有意义的,因为互斥体被设计为从多个线程访问,这些线程正在锁定、使用它保护的资源,然后解锁。
然而,在下面的代码中,我得到了一个编译器错误,据我所知,它抱怨互斥体没有实现Send/Sync:

error[E0599]: the method `try_init` exists for struct `SubscriberBuilder<DefaultFields, Format, tracing::level_filters::LevelFilter, std::sync::Mutex<MultiWriter>>`, but its trait bounds were not satisfied
   --> src/main.rs:131:10
    |
131 |           .try_init().expect("setting default subscriber failed");
    |            ^^^^^^^^ method cannot be called on `SubscriberBuilder<DefaultFields, Format, tracing::level_filters::LevelFilter, std::sync::Mutex<MultiWriter>>` due to unsatisfied trait bounds
    |
   ::: /Users/sean/.cargo/registry/src/github.com-1ecc6299db9ec823/tracing-subscriber-0.3.16/src/fmt/fmt_layer.rs:62:1
    |
62  | / pub struct Layer<
63  | |     S,
64  | |     N = format::DefaultFields,
65  | |     E = format::Format<format::Full>,
66  | |     W = fn() -> io::Stdout,
67  | | > {
    | | -
    | | |
    | |_doesn't satisfy `_: std::marker::Send`
    |   doesn't satisfy `_: std::marker::Sync`
    |
    = note: the following trait bounds were not satisfied:
            `tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, std::sync::Mutex<MultiWriter>>: std::marker::Send`
            `tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, std::sync::Mutex<MultiWriter>>: std::marker::Sync`

如果我从下面的代码中删除.with_writer(mw)行,错误就会消失。很明显,问题与作者有关,但我不确定如何正确地做到这一点。
代码的目标是将tracing框架中的日志写入stderr和dotenvy中指定的文件(如果指定了文件名,则为可选)。
注意:我使用的是最新的稳定版Rust和下面使用的每个机箱的发布版本,并在MacOS上用std,libc,alloc等(完整的Rust,而不是嵌入式)编译,但代码预计将在“多平台x86(_64)桌面”环境(Windows/MacOS/桌面Linux)下工作。

use std::fs::File;
use std::io::Write;
use std::sync::Mutex;

use dotenvy::var;
se std::sync::Arc;
use tracing::Level;

struct MultiWriter {
    writers: Vec<Arc<dyn Write>>,
}

impl Write for MultiWriter {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        for writer in self.writers.iter_mut() {
            writer.write(buf)?;
        }
        Ok(buf.len())
    }

    fn flush(&mut self) -> std::io::Result<()> {
        for writer in self.writers.iter_mut() {
            writer.flush()?;
        }
        Ok(())
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mut writers: Vec<Arc<dyn Write>> = vec![(Arc::new(std::io::stderr()))];
    if let Some(log_file) = var("log_file").ok() {
        writers.push(Arc::new(File::create(log_file).unwrap()));
    }
    let mw = Mutex::new(MultiWriter { writers });

    let tsb = tracing_subscriber::FmtSubscriber::builder()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()).with_ansi(false)
        .with_writer(mw);

    if let Ok(log_level) = var("log_level") {
        match log_level.to_uppercase().as_str() {
            "TRACE" => tsb.with_max_level(Level::TRACE),
            "DEBUG" => tsb.with_max_level(Level::DEBUG),
            "INFO" => tsb.with_max_level(Level::INFO),
            "WARN" => tsb.with_max_level(Level::WARN),
            "ERROR" => tsb.with_max_level(Level::ERROR),
            _ => tsb.with_max_level(Level::INFO)
        }
        .try_init().expect("setting default subscriber failed");
    }   
}
xxb16uws

xxb16uws1#

Mutex的文档中,它说它实现了发送和同步
这并不完全正确:

impl<T: ?Sized + Send> Send for Mutex<T>
impl<T: ?Sized + Send> Sync for Mutex<T>

这意味着只有当TSend时,互斥锁才为SendSync(原因在this question中描述。
然而,T在这里不是Send

  • Tstruct MultiWriter
  • struct MultiWriter包含一个
  • dyn Write不是Send(至少不总是如此)
  • 反过来,struct MultiWriter也不是。

要解决此问题,请将dyn Write替换为dyn Write + Send,它应该可以工作。

km0tfn4u

km0tfn4u2#

我认为需要强调的是OP做的是Mutex<Arc<MyType>>,这和Arc<Mutex<MyType>>是不同的,如果一个类型是Send,并且 Package 在一个Mutex中,那么整个事情就是Send + Sync,但是由于Arc两者都需要,这在这里很重要,但是如果Mutex在外面(就像这里一样),那么Arc并不满足于它里面的东西。
正如Elias所引用的(正确的),Mutex只要求它所包含的值是Send,而不是两个都是Send + Sync。OP的问题(其中之一)是Mutex在外面,Arc要求两个Sync + Send,而dyn Write不是。
弧:

#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}

互斥锁:

#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}

最终,@ChayimFriedman是正确的,对于OP的代码块,他们需要两者(dyn + Write + Send + Sync),但我想在这里写下更多关于 * 为什么 *。

相关问题