我在使用 Rust 和 GTK4 编写自己的蓝牙客户端时遇到了问题。
这就是想法。我有一个主事件循环,我想添加一个超时来每 5 秒轮询一个新的蓝牙设备,因为 GTK4 只是一个同步库。我使用 bluer 编写了一个异步函数来枚举蓝牙设备。该程序在其自己的具有异步主函数的程序中运行良好。采用相同的功能并将其移动到具有基本 GTK UI 的更大项目中(出于某种原因)不会将任何设备推送到消息队列。
在此上下文中的消息队列是
Arc<Mutex<Vec<String>>>
。只是在线程之间共享一个标准的 Mutex
。 sync_channels
和普通通道也会出现这种情况。
主线程尝试锁定
Mutex
,读取它,然后使用 drop(Mutex)
释放锁定。主循环上的事件每 5 秒运行一次。其余时间,我们的异步函数锁定互斥体并尝试将设备名称推送到消息队列,以便主线程可以读取它。
这是我到目前为止的代码。
pub fn build_ui(app: Application) -> Application {
app.connect_activate(move |app| {
let mut queue = Arc::new(Mutex::new(Vec::<String>::new()));
let mlayout = Grid::builder()
.height_request(700)
.width_request(200)
.build();
let text_buf = TextBuffer::new(None);
let text_view = TextView::builder()
.buffer(&text_buf)
.width_request(200)
.height_request(600)
.vexpand(true)
.build();
let mwindow = ApplicationWindow::builder()
.application(app)
.resizable(false)
.width_request(200)
.height_request(700)
.child(&mlayout)
.build();
let dev_list = Frame::builder()
.height_request(700)
.width_request(200)
.vexpand(true)
.child(&text_view)
.build();
let title = Label::builder()
.label("Device List")
.css_name("Title")
.width_request(200)
.height_request(50)
.justify(gtk4::Justification::Center)
.build();
mlayout.attach(&title, 0, 0, 200, 50);
mlayout.attach(&dev_list, 0, 50, 200, 1);
mwindow.init_layer_shell();
mwindow.set_anchor(Edge::Right, true);
mwindow.set_anchor(Edge::Top, true);
mwindow.present();
let txqueue = Arc::clone(&queue);
thread::spawn(move || async {
blue_init(txqueue).await;
});
gtk4::glib::timeout_add_local(Duration::from_secs(5), move || {
match queue.clone().try_lock() {
Ok(dev) => {
println!("{:#?}", dev);
drop(dev);
ControlFlow::Continue
}
_ => ControlFlow::Continue,
}
});
});
app
}
pub async fn blue_init(queue: Arc<Mutex<Vec<String>>>) {
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
runtime.spawn(async move {
let session = Session::new().await.unwrap();
let adapter = session.default_adapter().await.unwrap();
let discover = adapter.discover_devices().await.unwrap();
pin_mut!(discover);
while let Some(evt) = discover.next().await {
match evt {
AdapterEvent::DeviceAdded(addr) => {
let device = adapter.device(addr).unwrap();
let name = device.alias().await.unwrap();
match queue.try_lock() {
Ok(mut data) => {
data.push(name);
drop(data);
}
_ => {}
}
}
_ => {}
}
}
});
如有任何帮助,我们将不胜感激。
thread::spawn(move || async {
blue_init(txqueue).await;
});
这不执行任何操作:它启动一个新线程来初始化 future,但从不轮询它并立即返回。所以你的
blue_init
函数永远不会被调用(尝试在其中添加 println
来亲自查看)。
相反,您应该从
async
的声明中删除 blue_init
:
pub fn blue_init (queue: Arc<Mutex<Vec<String>>>) {
todo!();
}
并直接在线程中调用:
thread::spawn (move || blue_init (txqueue));