我有一个执行HTTP请求的函数。我在我编写的一个简单程序中使用了这个函数。我需要限制这些HTTP请求,以便我不超过速率限制。在JavaScript世界中,有一个非常方便的third party library,它提供了一个throttle
函数,它返回一个调用自己函数的新函数,但会将调用排队,这样它们每分钟只发生X次或者其他
是否有内置的C#方式,或者这是一个方便的模式或nuget pkg?
答案 0 :(得分:5)
没有内置方式。您需要找到一个库或自己实现它。
答案 1 :(得分:5)
如果您使用Reactive Extensions,则可以使用Observable.Throttle()
方法:https://msdn.microsoft.com/en-us/library/hh229400(v=vs.103).aspx
答案 2 :(得分:3)
正如其他答案所说,你必须自己实施。幸运的是,这很容易。就个人而言,我会创建一个Queue<ObjectWithYourFunction'sArguments>
。然后,您可能会有一个ThrottledFunction
排队,如果需要,可以启动后台任务以等待适当的时间。
完全未经测试的示例代码:
class ThrottleMyFunction
{
private class Arguments
{
public int coolInt;
public double whatever;
public string stringyThing;
}
private ConcurrentQueue<Arguments> _argQueue = new ConcurrentQueue<Arguments>();
private Task _loop;
//If you want to do "X times per minute", replace this argument with an int and use TimeSpan.FromMinutes(1/waitBetweenCalls)
public void ThrottleMyFunction(TimeSpan waitBetweenCalls)
{
_loop = Task.Factory.StartNew(() =>
{
Arguments args;
while (true)
{
if (_argQueue.TryDequeue(out args))
FunctionIWantToThrottle(args.coolInt, args.whatever, args.stringyThing);
}
Thread.Sleep(waitBetweenCalls);
});
}
public void ThrottledFunction(int coolerInt, double whatevs, string stringy)
{
_argQueue.Enqueue(new Arguments() { coolInt = coolerInt, whatever = whatevs, stringyThing = stringy });
}
}
答案 3 :(得分:1)
我写过这篇文章,但就其性质而言,测试会有点复杂。 (现在没有经过测试。)
假设你有一个Action<TParameters>
来调用,所以这个动作作为参数传递给构造函数。然后执行,你打电话给Enqueue(TParameters parameters)
。
它将使您的项目入队并开始处理队列(除非队列已被处理。)每次执行都会增加一个计数器,当计数器达到每个间隔的最大执行次数时,执行将停止。
还有指定间隔的计时器。当该计时器过去时,重置执行次数并处理队列(除非它已被处理。)这样就等待了等待间隔结束的项目。
这是对您的要求的字面解释。它不是在一个间隔内均匀地传播呼叫,它们之间有时间。这意味着如果你只能在一秒钟内进行3次特定呼叫,它将立即进行前3次呼叫,然后等到第二次呼叫再进行3次呼叫。目标是在不等待的情况下使用您拥有的任何容量,然后等待更多容量变为可用并使用它而无需等待。
using System;
using System.Collections.Concurrent;
using System.Threading;
using Timer = System.Timers.Timer;
namespace Throttler
{
public abstract class ExecutionThrottler<TParameters> : IDisposable
{
private readonly Action<TParameters> _action;
private readonly int _executionsPerInterval;
private readonly ConcurrentQueue<TParameters> _queue = new ConcurrentQueue<TParameters>();
private bool _processingQueue;
private readonly object _processingQueueLock = new object();
private int _executionsSinceIntervalStart;
private readonly Timer _timer;
bool _disposed;
protected ExecutionThrottler(Action<TParameters> action, TimeSpan interval, int executionsPerInterval)
{
_action = action;
_executionsPerInterval = executionsPerInterval;
_timer = new Timer(interval.TotalMilliseconds);
_timer.AutoReset = true;
_timer.Start();
_timer.Elapsed += OnIntervalEnd;
}
public void Enqueue(TParameters parameters)
{
_queue.Enqueue(parameters);
}
private void TryProcessQueue()
{
if (_processingQueue) return;
lock (_processingQueueLock)
{
if (_processingQueue) return;
_processingQueue = true;
try
{
ProcessQueue();
}
finally
{
_processingQueue = false;
}
}
}
private void ProcessQueue()
{
TParameters dequeuedParameters;
while ((_executionsSinceIntervalStart < _executionsPerInterval) && _queue.TryDequeue(out dequeuedParameters))
{
Interlocked.Increment(ref _executionsSinceIntervalStart);
_action.Invoke(dequeuedParameters);
}
}
private void OnIntervalEnd(object sender, System.Timers.ElapsedEventArgs e)
{
_executionsSinceIntervalStart = 0;
TryProcessQueue();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~ExecutionThrottler()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_timer.Dispose();
}
_disposed = true;
}
}
}
更新:编辑以删除不必要的Interlocked
使用以确保原子读/写。唯一需要它的操作是递增执行操作的计数。
答案 4 :(得分:0)
不,.Net Framework中没有内置限制功能。您需要构建自己的库或找到现有的库。
我认为您在Framework中最接近的是为Parallel.Foreach
之类的调用设置数字线程限制。
答案 5 :(得分:0)
这可以通过创建一个计时器和一个句柄来完成这个诀窍来实现,这里有一个如何链接http://sstut.com/csharpdotnet/javascript-timers-equivalent.php