当用户执行操作时使用 SignalR 向 UI 发送更新可能会导致频繁调用,我想限制这种情况。我不想在每次发生操作时都发送更新,而是希望仅在自上次操作后经过一定时间后才发送更新。
private async Task UserSessionUpdated(UserUpdateMessageUpdateCommand auto)
{
if (settings.interval is not null && settings.interval.Value > 0)
{
_stopwatch ??= new Stopwatch();
if (_stopwatch.ElapsedMilliseconds > _appSettings.interval.Value ||
_stopwatch.ElapsedMilliseconds == 0)
{
await HubContext.Clients.All.SendAsync("UserUpdateMessage", "load");
_stopwatch.Reset();
_stopwatch.Start();
}
}
else
{
await HubContext.Clients.All.SendAsync("UserUpdateMessage", "load");
}
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Bus.PubSub.SubscribeAsync<UserUpdateMessageUpdateCommand>("", UserSessionUpdated,
cancellationToken: stoppingToken);
}
虽然可以选择使用秒表,但它的缺点是即使一天没有任何操作,它也会持续滴答作响。
有没有办法为此目的实现节流机制?有人可以帮助我使用节流来实现这一目标吗?
您可以使用
Channel
来实现此目的 - 它们提供有界的要处理的项目队列。它是生产者 - 消费者模式的实现 - 在我们的例子中,我们不希望绑定队列,因此当队列已满时,它将被完全处理。
这是简单的实现,并附有代码解释。您可以使用它作为模板并将您的方法放在适当的位置:
// Sample DTO
public class SignalRMessageDto(DateTime dateTime)
{
public DateTime DateTime { get; set; } = dateTime;
}
// Create bounded channel, with capacity of 10 items.
// Get the reader and the writer.
var channel = Channel.CreateBounded<SignalRMessageDto>(new BoundedChannelOptions(10));
var reader = channel.Reader;
var writer = channel.Writer;
await WriteMessages();
async Task WriteMessages()
{
while (true)
{
var newMessage = new SignalRMessageDto(DateTime.Now);
// Try write message - if channel will be at capacity
// it won't accept new message and we will have
// information to flush reader.
var written = writer.TryWrite(newMessage);
if (written == false)
{
Console.WriteLine("Error when writing to channel");
await FlushReader();
// Now we should be able to write the message.
_ = writer.TryWrite(newMessage);
}
else
Console.WriteLine("Successfully written to channel");
await Task.Delay(100);
}
}
async Task FlushReader()
{
Console.WriteLine("Flushing reader!");
// We read and process all messages in channel.
await foreach (var item in reader.ReadAllAsync())
{
Console.WriteLine(item.DateTime);
// If we emptied channel, break.
if (reader.Count == 0) break;
}
}