C# 异步任务的有效速率限制(每个时间间隔执行N次)

C# 异步任务的有效速率限制(每个时间间隔执行N次),c#,asynchronous,async-await,c#-5.0,C#,Asynchronous,Async Await,C# 5.0,在使用.Net 4.0/4.5任务时,如何实现有效的速率限制算法(每个时间间隔执行N次特定操作)?例如,我希望SendMessageAsync在每秒发送的消息超过N条时阻止(不发送另一条消息) await Task.WhenAll( Enumerable.Range(0, TOTAL_MESSAGES) .Select(async x => {

在使用.Net 4.0/4.5任务时,如何实现有效的速率限制算法(每个时间间隔执行N次特定操作)?例如,我希望
SendMessageAsync
在每秒发送的消息超过N条时阻止(不发送另一条消息)

        await Task.WhenAll(
                    Enumerable.Range(0, TOTAL_MESSAGES)
                    .Select(async x =>
                    {
                        await clientSession.SendMessageAsync(CreateMessage());
                    }));
我曾尝试在
SendMessageAsync
内部使用
Task.Delay
,但由于等待
Task.Delay
会立即返回,因此下一条消息将在没有任何阻塞的情况下发送

public async Task<ResponseMessage> SendMessageAsync(RequestMessage message)
{

    int sleepTime;
    if (throttler.ShouldThrottle(out sleepTime))
    {
        await Task.Delay(sleepTime);
    }

    return await InternalMessageWithoutWaitingAsync(message);
}
公共异步任务SendMessageAsync(请求消息消息) { int睡眠时间; if(节流阀。应节流阀(超出睡眠时间)) { 等待任务。延迟(睡眠时间); } 返回waitinginternalmessagewithoutwaitingsync(消息); }
我可以将
wait Task.Delay(sleepTime)
更改为
Thread.Sleep(sleepTime)
从而在异步之前等待,但我想知道在使用任务时这是否是一个好的实践。

使用
线程。Sleep
异步
代码中不是一个好的实践

您可能会像这样得到您所需要的:

private SemaphoreSlim _semaphore = new SemaphoreSlim(N);
public async Task<ResponseMessage> SendMessageAsync(RequestMessage message)
{
  await _semaphore.WaitAsync();
  try
  {
    return await InternalMessageWithoutWaitingAsync(message);
  }
  finally
  {
    ReleaseSemaphoreAfterDelayAsync();
  }
}

private async Task ReleaseSemaphoreAfterDelayAsync()
{
  await Task.Delay(TimeInterval);
  _semaphore.Release();
}
private SemaphoreSlim\u semaphore=new SemaphoreSlim(N);
公共异步任务SendMessageAsync(RequestMessage消息)
{
wait_信号量。WaitAsync();
尝试
{
返回waitinginternalmessagewithoutwaitingsync(消息);
}
最后
{
ReleaseSamphoreAfterDelayAsync();
}
}
专用异步任务releaseSamphoreAfterDelayAsync()
{
等待任务。延迟(时间间隔);
_semaphore.Release();
}

只有当“节流器”发出“开始”信号时,我才会开始下一个任务。我认为这是一种比启动所有任务并立即等待大部分任务更好的模式。基于时间的操作(如在一个时间间隔内节流)最好使用Rx,而不是直接使用
async
。@robertmirca使用
Thread.Sleep
绝对是一种不好的做法(浪费资源)。我没有得到您关于
下一条消息将在没有任何阻止的情况下发送的担忧。这是否意味着您需要按顺序执行所有任务?如果您的场景非常复杂,您可以考虑实现自己的
任务调度器
:@outcoldman,正如@StephenCleary所提到的,问题是在一个时间间隔内进行节流,而不是在某个时刻限制并行操作(并发)的数量<代码>下一条消息将在没有任何阻塞的情况下发送
意味着下一条消息的发送不遵守我试图达到的1秒间隔内的速率限制。@robertmirca然后您可以实现自己的
任务调度程序
(如果您不想使用
Rx
)我使用这段代码生成了400K MSG。问题是,许多任务是使用800mb RAM创建的,而实际上只有N个任务在运行处理消息,其余任务则被信号量阻塞。在等待处理完成时,如何停止创建新任务wait Task.WhenAll(Enumerable.Range(04000).Select(异步x=>{var responsePdu=wait client.SendPduAsync(NewMessage());[…]})```当我在ANTS内存分析器中运行时,我看到400k任务占用17600000字节。可能是一些局部变量被提升导致了内存压力;请参阅Stephen Toub的参考资料和调优想法。我的想法如下:1)确保
InternalMessageWithingWithingAsync
是一个单独的方法,并且
SendMessageAsync
实际上就是上面的代码(即从该方法中删除所有本地变量);2) 您可以在代码中将信号量限制移到更高的位置;3) 重新考虑Rx<代码>异步
根本不是为此而设计的。感谢您的建议。我重新考虑Rx没有问题。我想要保留的唯一范例是给调用方使用任务的机会的美妙之处。库的设计是这样的:如果在30秒内没有从服务器返回回复,任务将被取消;如果与服务器通信出错,任务将捕获异常;或者任务将为调用方提供等待服务器回复的选项。生产者将消息“流”到库中(生产中的消息不限于预先已知的消息量)。我希望我能把它和Rx一起保存。@Prabhu:你可以等待它。请注意,VS2013不允许在
finally
(或catch)中使用
wait
,这样会使代码复杂化。预计VS的下一个版本将允许使用
finally
块进行
wait