我正在努力改进我的一些代码以提高效率。在原始代码中,我限制了允许为5的线程数,如果我已经有5个活动线程,我会等到一个完成后再启动另一个。现在我想修改此代码以允许任意数量的线程,但我希望能够确保每秒只启动5个线程。例如:
原始代码(cleanseDictionary通常包含数千项):
ConcurrentDictionary<long, APIResponse> cleanseDictionary = new ConcurrentDictionary<long, APIResponse>();
ConcurrentBag<int> itemsinsec = new ConcurrentBag<int>();
ConcurrentDictionary<long, string> resourceDictionary = new ConcurrentDictionary<long, string>();
DateTime start = DateTime.Now;
Parallel.ForEach(resourceDictionary, new ParallelOptions { MaxDegreeOfParallelism = 5 }, row =>
{
lock (itemsinsec)
{
ThrottleAPIRequests(itemsinsec, start);
itemsinsec.Add(1);
}
cleanseDictionary.TryAdd(row.Key, _helper.MakeAPIRequest(string.Format("/endpoint?{0}", row.Value)));
});
private static void ThrottleAPIRequests(ConcurrentBag<int> itemsinsec, DateTime start)
{
if ((start - DateTime.Now).Milliseconds < 10001 && itemsinsec.Count > 4)
{
System.Threading.Thread.Sleep(1000 - (start - DateTime.Now).Milliseconds);
start = DateTime.Now;
itemsinsec = new ConcurrentBag<int>();
}
}
我的第一个想法是将MaxDegreeofParallelism
增加到更高的值,然后有一个辅助方法,一秒钟内只限制5个线程,但我不确定这是否是最好的方法,如果是的,我可能需要lock
围绕这一步吗?
提前致谢!
修改 我实际上正在寻找一种方法来限制API请求而不是实际的线程。我以为他们是同一个人。
编辑2:我的要求是每秒发送5个以上的API请求
答案 0 :(得分:1)
&#34; Parallel.ForEach&#34;来自MS网站
可以并行运行
如果您想对线程的管理方式进行任何程度的精细控制,那就不是这样了 如何创建自己的帮助程序类,您可以使用组ID对作业进行排队,允许您等待组ID X的所有作业完成,并在需要时生成额外的线程?
答案 1 :(得分:0)
我希望能够每秒发送超过5个API请求
这真的很容易:
while (true) {
await Task.Delay(TimeSpan.FromSeconds(1));
await Task.WhenAll(Enumerable.Range(0, 5).Select(_ => RunRequestAsync()));
}
可能不是最好的方法,因为会有一连串的请求。这不是连续的。
此外,还有时间偏差。一次迭代需要1秒以上。这可以通过几行时间逻辑来解决。
答案 2 :(得分:0)
对我来说,最好的解决方案是:
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace SomeNamespace
{
public class RequestLimiter : IRequestLimiter
{
private readonly ConcurrentQueue<DateTime> _requestTimes;
private readonly TimeSpan _timeSpan;
private readonly object _locker = new object();
public RequestLimiter()
{
_timeSpan = TimeSpan.FromSeconds(1);
_requestTimes = new ConcurrentQueue<DateTime>();
}
public TResult Run<TResult>(int requestsOnSecond, Func<TResult> function)
{
WaitUntilRequestCanBeMade(requestsOnSecond).Wait();
return function();
}
private Task WaitUntilRequestCanBeMade(int requestsOnSecond)
{
return Task.Factory.StartNew(() =>
{
while (!TryEnqueueRequest(requestsOnSecond).Result) ;
});
}
private Task SynchronizeQueue()
{
return Task.Factory.StartNew(() =>
{
_requestTimes.TryPeek(out var first);
while (_requestTimes.Count > 0 && (first.Add(_timeSpan) < DateTime.UtcNow))
_requestTimes.TryDequeue(out _);
});
}
private Task<bool> TryEnqueueRequest(int requestsOnSecond)
{
lock (_locker)
{
SynchronizeQueue().Wait();
if (_requestTimes.Count < requestsOnSecond)
{
_requestTimes.Enqueue(DateTime.UtcNow);
return Task.FromResult(true);
}
return Task.FromResult(false);
}
}
}
}