介绍:
在开发过程中,我们可能会遇到错误消息: “error: future cannot be sent between threads safely”。这个错误通常是由于使用了不可发送(not Send)的类型引起的,尤其是 std::sync::MutexGuard
类型。这意味着我们不能将一个互斥锁(mutex)在不同线程间进行传递,而 Tokio 运行时又允许任务在每个 .await
点之间在不同线程间移动。因此,我们需要重新组织代码以确保互斥锁的析构函数在 .await
之前运行,以避免出现此错误。
还有另外一种方案是使用 tokio::task::LocalSet
。通过使用 LocalSet
,你可以确保异步任务只在单个线程上运行,而不需要实现 Send
。这对于需要任务始终在同一线程上访问特定资源的情况非常有用。
范例一:
下面我们提供了一个范例来说明可以解决该问题的正确代码和错误代码。
正确代码:
use std::sync::{Mutex, MutexGuard};
async fn ictester_main(mutex: &Mutex<i32>) {
{
let mut lock: MutexGuard<i32> = mutex.lock().unwrap();
*lock += 1;
} // 在这里,互斥锁的析构函数会在.await之前运行,并释放锁
http_get_request_async().await;
}
错误代码:
use std::sync::{Mutex, MutexGuard};
async fn ictester_main(mutex: &Mutex<i32>) {
let mut lock: MutexGuard<i32> = mutex.lock().unwrap();
*lock += 1;
drop(lock); // 这种方式是错误的
http_get_request_async().await;
}
范例二:
另一种解决方案是使用 tokio::task::LocalSet
。通过使用 LocalSet
,你可以确保异步任务只在单个线程上运行,而不需要实现 Send
。这对于需要任务始终在同一线程上访问特定资源的情况非常有用。
use tokio::task::LocalSet;
// ...
let mut local_set = LocalSet::new();
local_set.spawn_local(ictester_main(yourI32Param.clone()));
let out = local_set.await;
在上面的代码中,我们创建了一个 LocalSet
,然后使用 spawn_local()
方法将异步任务 ictester_main()
添加到 LocalSet
中。这确保了该任务将在同一线程上运行,从而避免了错误:“error: future cannot be sent between threads safely”。
注意事项
- 在使用互斥锁(
Mutex
)时,务必在任务的关键点上正确释放锁。最佳实践是确保互斥锁的析构函数在.await
之前运行,以确保锁被正确释放。可以通过在互斥锁的作用域内使用大括号来控制互斥锁的生命周期。 - 避免在异步任务中手动调用
drop
函数来释放互斥锁。手动调用drop
函数会导致互斥锁被提前释放,可能会导致错误,因为互斥锁不应该在异步的上下文中手动释放。 - 当使用
tokio::task::LocalSet
来确保异步任务在同一线程上运行时,需要注意,该方式适用于在同一线程上访问特定资源的情况。
总结:
在解决 “error: future cannot be sent between threads safely” 类似的错误时,我们应该避免将不可发送类型引入跨线程的异步任务中。这可以通过确保互斥锁的析构函数在 .await
之前运行,或者使用 tokio::task::LocalSet
等方法来限制任务的运行范围。通过合理组织代码,我们可以避免这种错误,并确保程序的线程安全性。
除了通过重新组织代码以确保互斥锁在 .await
前释放以避免错误外,还可以使用 tokio::task::LocalSet
来限制异步任务的运行范围。这样可以确保任务始终在同一线程上运行,而无需实现 Send
。根据具体情况选择适合的解决方案,以确保代码的正确性和线程安全性。