pub struct Semaphore { /* private fields */ }展开描述
执行异步 permit 获取的计数信号量(counting semaphore)。
semaphore 维护一组 permit。 permit 用于同步 对共享资源的访问。 semaphore 与 mutex 的不同之处 在于它 可以允许 一个以上的并发调用者 同时访问 共享资源。
当
被调用,
且 semaphore
还有剩余 permit 时,
该函数
会立即
返回一个 permit。
但是,
如果没有剩余 permit,
acquire
会(异步地)等待,
直到
一个未完成的 permit
被丢弃。
此时,
释放出的 permit
会被分配给调用者。acquire
此
是公平的,
这意味着 permit
按
请求它们的顺序
分发。
当
Semaphore
涉及时,
这种公平性
也适用,
因此如果
队列前端对
acquire_many
的调用请求
比当前可用的
更多的 permit,
这
可能会
阻止
对
acquire_many
的调用
完成,
即使
semaphore
有足够的 permit
来完成该
对
acquire
的调用。acquire
要在 poll 函数
中使用
,
可以使用
Semaphore
实用工具。PollSemaphore
§示例
基本用法:
use tokio::sync::{Semaphore, TryAcquireError};
let semaphore = Semaphore::new(3);
let a_permit = semaphore.acquire().await.unwrap();
let two_permits = semaphore.acquire_many(2).await.unwrap();
assert_eq!(semaphore.available_permits(), 0);
let permit_attempt = semaphore.try_acquire();
assert_eq!(permit_attempt.err(), Some(TryAcquireError::NoPermits));§限制程序中同时打开的文件数
大多数操作系统 对打开的文件句柄 数量有限制。 即使在 没有明确限制的系统中, 资源约束 也会 隐式设置 打开文件数量的上限。 如果你的程序 尝试打开 大量文件 并超过此限制, 将导致错误。
此示例
使用
具有 100 个 permit 的
Semaphore。
在访问文件
之前
从
Semaphore
获取一个 permit,
你可以
确保你的程序
一次打开
不超过 100 个文件。
当尝试打开
第 101 个文件时,
程序
将等待
直到
permit 可用,
然后才
继续打开
另一个文件。
use std::io::Result;
use tokio::fs::File;
use tokio::sync::Semaphore;
use tokio::io::AsyncWriteExt;
static PERMITS: Semaphore = Semaphore::const_new(100);
async fn write_to_file(message: &[u8]) -> Result<()> {
let _permit = PERMITS.acquire().await.unwrap();
let mut buffer = File::create("example.txt").await?;
buffer.write_all(message).await?;
Ok(()) // Permit goes out of scope here, and is available again for acquisition
}§限制同时发送的出站请求数
在某些场景下, 可能需要 限制 并行发送的 传出请求数量。 这可能是由于 所使用 API 的限制, 或应用程序运行的 系统 网络资源的限制。
此示例
使用
具有 10 个 permit 的
。
每个
派生的任务
通过克隆
Arc<Semaphore>
获取对 semaphore 的引用。
任务
发送请求之前,
必须
通过调用
Arc<Semaphore>
从 semaphore
获取一个 permit。
这
确保
最多 10 个请求
在任何给定时间
并行发送。
任务
发送请求之后,
它
会
丢弃该 permit,
以允许其他任务
发送请求。Semaphore::acquire
use std::sync::Arc;
use tokio::sync::Semaphore;
// Define maximum number of parallel requests.
let semaphore = Arc::new(Semaphore::new(5));
// Spawn many tasks that will send requests.
let mut jhs = Vec::new();
for task_id in 0..50 {
let semaphore = semaphore.clone();
let jh = tokio::spawn(async move {
// Acquire permit before sending request.
let _permit = semaphore.acquire().await.unwrap();
// Send the request.
let response = send_request(task_id).await;
// Drop the permit after the request has been sent.
drop(_permit);
// Handle response.
// ...
response
});
jhs.push(jh);
}
// Collect responses from tasks.
let mut responses = Vec::new();
for jh in jhs {
let response = jh.await.unwrap();
responses.push(response);
}
// Process responses.
// ...§限制同时处理的入站请求数
类似于 限制同时打开的文件数, 网络句柄 是一种有限的资源。 允许 无限制数量的请求 被处理 可能会 导致拒绝服务, 以及许多其他问题。
此示例
使用
而不是
全局变量。
为了
限制
可同时
处理的请求数,
a new task is spawned; and once finished, the permit is dropped inside
of the task to allow others to spawn. Permits must be acquired via
Arc<Semaphore> to be movable across the task boundary.
(Since our semaphore is not a global variable — if it was, then Semaphore::acquire_owned would be enough.)acquire
use std::sync::Arc;
use tokio::sync::Semaphore;
use tokio::net::TcpListener;
#[tokio::main]
async fn main() -> std::io::Result<()> {
let semaphore = Arc::new(Semaphore::new(3));
let listener = TcpListener::bind("127.0.0.1:8080").await?;
loop {
// Acquire permit before accepting the next socket.
//
// We use `acquire_owned` so that we can move `permit` into
// other tasks.
let permit = semaphore.clone().acquire_owned().await.unwrap();
let (mut socket, _) = listener.accept().await?;
tokio::spawn(async move {
// Do work using the socket.
handle_connection(&mut socket).await;
// Drop socket while the permit is still live.
drop(socket);
// Drop the permit, so more tasks can be created.
drop(permit);
});
}
}§防止测试并行运行
默认情况下, Rust 并行运行 同一文件中的测试。 但是, 在某些情况下, 并行运行两个测试 可能会导致问题。 例如, 当测试 使用 同一个数据库时, 就可能发生这种情况。
考虑以下场景:
test_insert: Inserts a key-value pair into the database, then retrieves the value using the same key to verify the insertion.test_update: Inserts a key, then updates the key to a new value and verifies that the value has been accurately updated.test_others: A third test that doesn’t modify the database state. It can run in parallel with the other tests.
在此示例中,
和
test_insert
需要按顺序运行
才能正常工作,
但哪个测试
先运行
并不重要。
我们可以利用
具有单个 permit 的
semaphore
来解决此问题。test_update
use tokio::sync::Semaphore;
// Initialize a static semaphore with only one permit, which is used to
// prevent test_insert and test_update from running in parallel.
static PERMIT: Semaphore = Semaphore::const_new(1);
// Initialize the database that will be used by the subsequent tests.
static DB: Database = Database::setup();
#[tokio::test]
async fn test_insert() {
// Acquire permit before proceeding. Since the semaphore has only one permit,
// the test will wait if the permit is already acquired by other tests.
let permit = PERMIT.acquire().await.unwrap();
// Do the actual test stuff with database
// Insert a key-value pair to database
let (key, value) = ("name", 0);
DB.insert(key, value).await;
// Verify that the value has been inserted correctly.
assert_eq!(DB.get(key).await, value);
// Undo the insertion, so the database is empty at the end of the test.
DB.delete(key).await;
// Drop permit. This allows the other test to start running.
drop(permit);
}
#[tokio::test]
async fn test_update() {
// Acquire permit before proceeding. Since the semaphore has only one permit,
// the test will wait if the permit is already acquired by other tests.
let permit = PERMIT.acquire().await.unwrap();
// Do the same insert.
let (key, value) = ("name", 0);
DB.insert(key, value).await;
// Update the existing value with a new one.
let new_value = 1;
DB.update(key, new_value).await;
// Verify that the value has been updated correctly.
assert_eq!(DB.get(key).await, new_value);
// Undo any modificattion.
DB.delete(key).await;
// Drop permit. This allows the other test to start running.
drop(permit);
}
#[tokio::test]
async fn test_others() {
// This test can run in parallel with test_insert and test_update,
// so it does not use PERMIT.
}§使用令牌桶进行限流
此示例
展示了
和
add_permits
方法。SemaphorePermit::forget
许多应用程序 和系统 对 某些操作 应发生的 速率 有限制。 超过此速率 可能会导致 次优的性能 甚至错误。
此示例 使用 token bucket 实现速率限制。 token bucket 是一种 速率限制形式, 不会立即生效, 以便 允许 同时到达的传入请求 出现 短暂的突发。
对于 token bucket, 每个传入请求 消耗一个 token, 且 token 以 定义速率限制的 特定速率 被补充。 当一批请求 到达时, token 会立即分发, 直到 bucket 为空。 一旦 bucket 为空, 请求必须 等待 新 token 被添加。
与 限制同时可处理 请求数的示例不同, 我们 不会在 完成处理请求后 将 token 加回。 相反, token 仅由 定时器任务 添加。
请注意, 当持续时间较小时, 此实现 是次优的, 因为它 会消耗大量 cpu 来持续循环和睡眠。
use std::sync::Arc;
use tokio::sync::Semaphore;
use tokio::time::{interval, Duration};
struct TokenBucket {
sem: Arc<Semaphore>,
jh: tokio::task::JoinHandle<()>,
}
impl TokenBucket {
fn new(duration: Duration, capacity: usize) -> Self {
let sem = Arc::new(Semaphore::new(capacity));
// refills the tokens at the end of each interval
let jh = tokio::spawn({
let sem = sem.clone();
let mut interval = interval(duration);
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
async move {
loop {
interval.tick().await;
if sem.available_permits() < capacity {
sem.add_permits(1);
}
}
}
});
Self { jh, sem }
}
async fn acquire(&self) {
// This can return an error if the semaphore is closed, but we
// never close it, so this error can never happen.
let permit = self.sem.acquire().await.unwrap();
// To avoid releasing the permit back to the semaphore, we use
// the `SemaphorePermit::forget` method.
permit.forget();
}
}
impl Drop for TokenBucket {
fn drop(&mut self) {
// Kill the background task so it stops taking up resources when we
// don't need it anymore.
self.jh.abort();
}
}
let capacity = 5;
let update_interval = Duration::from_secs_f32(1.0 / capacity as f32);
let bucket = TokenBucket::new(update_interval, capacity);
for _ in 0..5 {
bucket.acquire().await;
// do the operation
}实现§
Source§impl Semaphore
impl Semaphore
Sourcepub const MAX_PERMITS: usize = super::batch_semaphore::Semaphore::MAX_PERMITS
pub const MAX_PERMITS: usize = super::batch_semaphore::Semaphore::MAX_PERMITS
信号量可持有的最大许可证数量。其值为 usize::MAX >> 3。
超出此限制通常会导致 panic。
Sourcepub fn new(permits: usize) -> Self
pub fn new(permits: usize) -> Self
创建具有初始许可证数量的信号量。
如果 permits 超过 Semaphore::MAX_PERMITS 则会 panic。
Sourcepub const fn const_new(permits: usize) -> Self
pub const fn const_new(permits: usize) -> Self
创建具有初始许可证数量的信号量。
使用 tracing 不稳定特性时,通过 const_new 创建的 Semaphore 不会被插桩。因此,它不会出现在 tokio-console 中。如有需要,请改用 Semaphore::new 来创建可插桩的对象。
§示例
use tokio::sync::Semaphore;
static SEM: Semaphore = Semaphore::const_new(10);Sourcepub fn available_permits(&self) -> usize
pub fn available_permits(&self) -> usize
返回当前可用的许可证数量。
Sourcepub fn add_permits(&self, n: usize)
pub fn add_permits(&self, n: usize)
向信号量添加 n 个新许可证。
最大许可证数量为 Semaphore::MAX_PERMITS,如果超出该限制,此函数将 panic。
Sourcepub fn forget_permits(&self, n: usize) -> usize
pub fn forget_permits(&self, n: usize) -> usize
最多减少信号量 n 个许可证。
如果没有足够的许可证且无法减少 n,则返回实际减少的许可证数量。
Sourcepub async fn acquire(&self) -> Result<SemaphorePermit<'_>, AcquireError>
pub async fn acquire(&self) -> Result<SemaphorePermit<'_>, AcquireError>
从信号量获取一个许可证。
如果信号量已被关闭,则返回 AcquireError。否则,返回代表已获取许可证的 SemaphorePermit。
§Cancel safety
此方法使用队列按请求顺序公平分配许可证。取消对 acquire 的调用会丢失在队列中的位置。
§示例
use tokio::sync::Semaphore;
let semaphore = Semaphore::new(2);
let permit_1 = semaphore.acquire().await.unwrap();
assert_eq!(semaphore.available_permits(), 1);
let permit_2 = semaphore.acquire().await.unwrap();
assert_eq!(semaphore.available_permits(), 0);
drop(permit_1);
assert_eq!(semaphore.available_permits(), 1);Sourcepub async fn acquire_many(
&self,
n: u32,
) -> Result<SemaphorePermit<'_>, AcquireError>
pub async fn acquire_many( &self, n: u32, ) -> Result<SemaphorePermit<'_>, AcquireError>
从信号量获取 n 个许可证。
如果信号量已被关闭,则返回 AcquireError。否则,返回代表已获取许可证的 SemaphorePermit。
§Cancel safety
此方法使用队列按请求顺序公平分配许可证。取消对 acquire_many 的调用会丢失在队列中的位置。
§示例
use tokio::sync::Semaphore;
let semaphore = Semaphore::new(5);
let permit = semaphore.acquire_many(3).await.unwrap();
assert_eq!(semaphore.available_permits(), 2);Sourcepub fn try_acquire(&self) -> Result<SemaphorePermit<'_>, TryAcquireError>
pub fn try_acquire(&self) -> Result<SemaphorePermit<'_>, TryAcquireError>
尝试从信号量获取一个许可证。
如果信号量已被关闭,则返回 TryAcquireError::Closed;如果没有剩余许可证则返回 TryAcquireError::NoPermits。否则,返回代表已获取许可证的 SemaphorePermit。
§示例
use tokio::sync::{Semaphore, TryAcquireError};
let semaphore = Semaphore::new(2);
let permit_1 = semaphore.try_acquire().unwrap();
assert_eq!(semaphore.available_permits(), 1);
let permit_2 = semaphore.try_acquire().unwrap();
assert_eq!(semaphore.available_permits(), 0);
let permit_3 = semaphore.try_acquire();
assert_eq!(permit_3.err(), Some(TryAcquireError::NoPermits));Sourcepub fn try_acquire_many(
&self,
n: u32,
) -> Result<SemaphorePermit<'_>, TryAcquireError>
pub fn try_acquire_many( &self, n: u32, ) -> Result<SemaphorePermit<'_>, TryAcquireError>
尝试从信号量获取 n 个许可证。
如果信号量已被关闭,则返回 TryAcquireError::Closed;如果没有足够的许可证则返回 TryAcquireError::NoPermits。否则,返回代表已获取许可证的 SemaphorePermit。
§示例
use tokio::sync::{Semaphore, TryAcquireError};
let semaphore = Semaphore::new(4);
let permit_1 = semaphore.try_acquire_many(3).unwrap();
assert_eq!(semaphore.available_permits(), 1);
let permit_2 = semaphore.try_acquire_many(2);
assert_eq!(permit_2.err(), Some(TryAcquireError::NoPermits));Sourcepub async fn acquire_owned(
self: Arc<Self>,
) -> Result<OwnedSemaphorePermit, AcquireError>
pub async fn acquire_owned( self: Arc<Self>, ) -> Result<OwnedSemaphorePermit, AcquireError>
从信号量获取一个许可证。
调用此方法时信号量必须包装在 Arc 中。如果信号量已被关闭,则返回 AcquireError。否则,返回代表已获取许可证的 OwnedSemaphorePermit。
§Cancel safety
此方法使用队列按请求顺序公平分配许可证。取消对 acquire_owned 的调用会丢失在队列中的位置。
§示例
use std::sync::Arc;
use tokio::sync::Semaphore;
let semaphore = Arc::new(Semaphore::new(3));
let mut join_handles = Vec::new();
for _ in 0..5 {
let permit = semaphore.clone().acquire_owned().await.unwrap();
join_handles.push(tokio::spawn(async move {
// perform task...
// explicitly own `permit` in the task
drop(permit);
}));
}
for handle in join_handles {
handle.await.unwrap();
}Sourcepub async fn acquire_many_owned(
self: Arc<Self>,
n: u32,
) -> Result<OwnedSemaphorePermit, AcquireError>
pub async fn acquire_many_owned( self: Arc<Self>, n: u32, ) -> Result<OwnedSemaphorePermit, AcquireError>
从信号量获取 n 个许可证。
调用此方法时信号量必须包装在 Arc 中。如果信号量已被关闭,则返回 AcquireError。否则,返回代表已获取许可证的 OwnedSemaphorePermit。
§Cancel safety
此方法使用队列按请求顺序公平分配许可证。取消对 acquire_many_owned 的调用会丢失在队列中的位置。
§示例
use std::sync::Arc;
use tokio::sync::Semaphore;
let semaphore = Arc::new(Semaphore::new(10));
let mut join_handles = Vec::new();
for _ in 0..5 {
let permit = semaphore.clone().acquire_many_owned(2).await.unwrap();
join_handles.push(tokio::spawn(async move {
// perform task...
// explicitly own `permit` in the task
drop(permit);
}));
}
for handle in join_handles {
handle.await.unwrap();
}Sourcepub fn try_acquire_owned(
self: Arc<Self>,
) -> Result<OwnedSemaphorePermit, TryAcquireError>
pub fn try_acquire_owned( self: Arc<Self>, ) -> Result<OwnedSemaphorePermit, TryAcquireError>
尝试从信号量获取一个许可证。
调用此方法时信号量必须包装在 Arc 中。如果信号量已被关闭,则返回 TryAcquireError::Closed;如果没有剩余许可证则返回 TryAcquireError::NoPermits。否则,返回代表已获取许可证的 OwnedSemaphorePermit。
§示例
use std::sync::Arc;
use tokio::sync::{Semaphore, TryAcquireError};
let semaphore = Arc::new(Semaphore::new(2));
let permit_1 = Arc::clone(&semaphore).try_acquire_owned().unwrap();
assert_eq!(semaphore.available_permits(), 1);
let permit_2 = Arc::clone(&semaphore).try_acquire_owned().unwrap();
assert_eq!(semaphore.available_permits(), 0);
let permit_3 = semaphore.try_acquire_owned();
assert_eq!(permit_3.err(), Some(TryAcquireError::NoPermits));Sourcepub fn try_acquire_many_owned(
self: Arc<Self>,
n: u32,
) -> Result<OwnedSemaphorePermit, TryAcquireError>
pub fn try_acquire_many_owned( self: Arc<Self>, n: u32, ) -> Result<OwnedSemaphorePermit, TryAcquireError>
尝试从信号量获取 n 个许可证。
调用此方法时信号量必须包装在 Arc 中。如果信号量已被关闭,则返回 TryAcquireError::Closed;如果没有剩余许可证则返回 TryAcquireError::NoPermits。否则,返回代表已获取许可证的 OwnedSemaphorePermit。
§示例
use std::sync::Arc;
use tokio::sync::{Semaphore, TryAcquireError};
let semaphore = Arc::new(Semaphore::new(4));
let permit_1 = Arc::clone(&semaphore).try_acquire_many_owned(3).unwrap();
assert_eq!(semaphore.available_permits(), 1);
let permit_2 = semaphore.try_acquire_many_owned(2);
assert_eq!(permit_2.err(), Some(TryAcquireError::NoPermits));Sourcepub fn close(&self)
pub fn close(&self)
关闭信号量。
这会阻止信号量发放新许可证,并通知所有待处理的等待者。
§示例
use tokio::sync::Semaphore;
use std::sync::Arc;
use tokio::sync::TryAcquireError;
let semaphore = Arc::new(Semaphore::new(1));
let semaphore2 = semaphore.clone();
tokio::spawn(async move {
let permit = semaphore.acquire_many(2).await;
assert!(permit.is_err());
println!("waiter received error");
});
println!("closing semaphore");
semaphore2.close();
// Cannot obtain more permits
assert_eq!(semaphore2.try_acquire().err(), Some(TryAcquireError::Closed))