How to share AppState between threads? #8538
-
Hey, I am building an app where users can upload a ton of files. I'm currently stuck at developing a feature called 'clear the upload queue.' When a user selects a file and clicks 'upload' from the system tray menu option, I spin the upload function as a separate thread. Then, when the user stops clicking the button, I use commands to mutate the main state. The problem I'm facing is how to share the same main state between different threads. Here is my code. Am I doing something wrong? Is there a better way to share the main state between threads? main.rs #[derive(Clone)]
pub struct MainState {
pub stop: Arc<Mutex<bool>>,
}
fn main() {
tauri::Builder::default()
.manage(MainState {
stop: Arc::new(Mutex::new(false)),
})
.system_tray(setup_system_tray())
.on_system_tray_event(|app, event| handle_system_tray_event(app, event, app.state()))
....
}
pub fn handle_system_tray_event(app: &AppHandle, event: SystemTrayEvent, state: State<MainState>) {
match event {
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
"upload" => {
// let state_data = state.inner().clone();
tauri::async_runtime::spawn(start_upload(
app.clone(),
state,
));
}
_ => {}
},
_ => {}
}
} Error: |
28 | pub fn handle_system_tray_event(app: &AppHandle, event: SystemTrayEvent, state: State<MainState>) {
| -----
| |
| `state` is a reference that is only valid in the function body
| has type `tauri::State<'1, MainState>`
...
33 | tauri::async_runtime::spawn(start_upload(
| _____________________________________________^
34 | | app.clone(),
35 | | state,
36 | | ));
| | ^
| | |
| |_________________`state` escapes the function body here
| argument requires that `'1` must outlive `'static` |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
in my experience it's easier to move an AppHandle into threads and tasks and use that to get the state on demand. In your case you already give start_upload an AppHandle instance (probably to emit events?), so just remove the state parameter and do something like this inside your start_upload function start_upload(app_handle: AppHandle) {
let state = app_handle.state::<MainState>();
// ...
} |
Beta Was this translation helpful? Give feedback.
-
Multi-thread shared variables, use For example, in file use std::sync::{Arc, Mutex};
use std::thread;
pub struct MyStateArc {
pub count: i32,
}
#[tauri::command]
pub fn read_write_my_state_arc(my_state_arc: tauri::State<Arc<Mutex<MyStateArc>>>) -> String {
// 10 threads
for _ in 0..10 {
let my_state_arc_clone = Arc::clone(&my_state_arc); // Note: clone arc to every thread, and share my_state_arc
tauri::async_runtime::spawn(async move {
let mut num = my_state_arc_clone.lock().unwrap();
num.count = num.count + 1;
});
}
let num = my_state_arc.lock().unwrap();
format!("couter is {}", num.count)
} in file fn main() {
tauri::Builder::default()
.setup(|app| {
let my_sate_arc = Arc::new(Mutex::new(MyStateArc { count: 0 }));
app.manage(my_sate_arc);
})
.invoke_handler(tauri::generate_handler![
mystatearc::read_write_my_state_arc,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
} you can also define like this: pub struct MyStateArc {
pub count: Arc<Mutex<i32>>,
} and use like this: let arc_count = Arc::clone(&my_state_arc.count);
tauri::async_runtime::spawn(async move {
let mut num = arc_count.lock().unwrap();
count = count + 1;
}); |
Beta Was this translation helpful? Give feedback.
in my experience it's easier to move an AppHandle into threads and tasks and use that to get the state on demand. In your case you already give start_upload an AppHandle instance (probably to emit events?), so just remove the state parameter and do something like this inside your start_upload function