3

TPL/Parallel.ForEach를 사용하면 단위 시간당 메소드 호출 횟수를 제한 할 수있는 기본 방법이 있습니다 (즉, 둘째). 이는 스레드 수를 제한하는 것과는 다릅니다. 아마도이 작업을 수행하기위한 간단한 해킹이있을 것입니다.병렬 호출에서 초당 실행을 제한하십시오.

+0

정확히 무엇이 필요하며 왜 필요한지 더 자세히 설명해 주시겠습니까? – svick

+0

웹 API를 병렬로 호출해야하지만 API는 초당 호출 수를 제한합니다. 나는 그 한계에 머무르고 싶다. – SFun28

답변

2

하나의 솔루션을 사용하여 다음 https://stackoverflow.com/a/7728872/356790

/// <summary> 
/// This class limits the number of requests (method calls, events fired, etc.) that can occur in a given unit of time. 
/// </summary> 
class RequestLimiter 
{ 

    #region Constructors 

    /// <summary> 
    /// Initializes an instance of the RequestLimiter class. 
    /// </summary> 
    /// <param name="maxRequests">The maximum number of requests that can be made in a given unit of time.</param> 
    /// <param name="timeSpan">The unit of time that the maximum number of requests is limited to.</param> 
    /// <exception cref="ArgumentException">maxRequests &lt;= 0</exception> 
    /// <exception cref="ArgumentException">timeSpan.TotalMilliseconds &lt;= 0</exception> 
    public RequestLimiter(int maxRequests , TimeSpan timeSpan) 
    { 
     // check parameters 
     if (maxRequests <= 0) 
     { 
      throw new ArgumentException("maxRequests <= 0" , "maxRequests"); 
     } 
     if (timeSpan.TotalMilliseconds <= 0) 
     { 
      throw new ArgumentException("timeSpan.TotalMilliseconds <= 0" , "timeSpan"); 
     } 

     // initialize instance vars 
     _maxRequests = maxRequests; 
     _timeSpan = timeSpan; 
     _requestTimes = new Queue<DateTime>(maxRequests); 

     // sleep for 1/10th timeSpan 
     _sleepTimeInMs = Convert.ToInt32(Math.Ceiling(timeSpan.TotalMilliseconds/10)); 
    } 

    #endregion 

    /// <summary> 
    /// Waits until an request can be made 
    /// </summary> 
    public void WaitUntilRequestCanBeMade() 
    { 
     while (!TryEnqueueRequest()) 
     { 
      Thread.Sleep(_sleepTimeInMs); 
     } 
    } 

    #region Private Members 

    private readonly Queue<DateTime> _requestTimes; 
    private readonly object _requestTimesLock = new object(); 
    private readonly int _maxRequests; 
    private readonly TimeSpan _timeSpan; 
    private readonly int _sleepTimeInMs; 

    /// <summary> 
    /// Remove requests that are older than _timeSpan 
    /// </summary> 
    private void SynchronizeQueue() 
    { 
     while ((_requestTimes.Count > 0) && (_requestTimes.Peek().Add(_timeSpan) < DateTime.Now)) 
     { 
      _requestTimes.Dequeue(); 
     } 
    } 

    /// <summary> 
    /// Attempts to enqueue a request. 
    /// </summary> 
    /// <returns> 
    /// Returns true if the request was successfully enqueued. False if not. 
    /// </returns> 
    private bool TryEnqueueRequest() 
    { 
     lock (_requestTimesLock) 
     { 
      SynchronizeQueue(); 
      if (_requestTimes.Count < _maxRequests) 
      { 
       _requestTimes.Enqueue(DateTime.Now); 
       return true; 
      } 
      return false; 
     } 
    } 

    #endregion 

} 
+0

다른 질문에 답한 것 같습니다. "실행을 제한하는"필요성 ("웹 API를 병렬로 호출")의 필요성 (http://stackoverflow.com/questions/16360733/in-parallel-call-limit-executions-per-second/16402417#comment23442116_16360733))는 제한보다 더 많은 실행/요청을하고 대기열에 대기열을 넣거나 대기열에 넣는 것을 지연시키는 것과 같은 초고속 복잡성 **을 의미하지 않습니다! –

+0

나는 당신이 무엇을 말하려고하는지 전혀 모른다. 호출 횟수를 X/초로 제한하려고합니다. 이것은 API 호출을하기 전에 WaitUntilRequestCanBeMade()를 호출하여 수행됩니다. – SFun28

+0

실행 횟수 (호출 등)를 제한하기 위해 대기열/큐에서 대기열에 넣을 필요가 없습니다. 필요한 제한보다 초당 더 많은 전화를 걸지 마십시오. 저것과 같이 쉬운! [내 대답은] (http://stackoverflow.com/a/16402417/200449) 나는 대기열/de-queuing없이 이러한 실현의 준비 코드 예제에 대한 참조를 주었다. 호출의 흐름이 통제 범위를 벗어난다면 귀하의 답변은 유효한 접근 방법이지만 게시 된 질문과는 아무런 관련이 없습니다. 당신은 질문을 게시하고 (다른 질문에 답을하고 그것을 받아들이십시오!) –

0

이 솔루션은 각 스레드의 시작 사이에 지연을 적용하고 요구 사항을 채우십시오.

private SemaphoreSlim CooldownLock = new SemaphoreSlim(1, 1); 
    private DateTime lastAction; 

    private void WaitForCooldown(TimeSpan delay) 
    { 
     CooldownLock.Wait(); 

     var waitTime = delay - (DateTime.Now - lastAction); 

     if (waitTime > TimeSpan.Zero) 
     { 
      Task.Delay(waitTime).Wait(); 
      lastAction = DateTime.Now; 
     } 

     lastAction = DateTime.Now; 

     CooldownLock.Release(); 
    } 

    public void Execute(Action[] actions, int concurrentThreadLimit, TimeSpan threadDelay) 
    { 
     if (actions.Any()) 
     { 
      Parallel.ForEach(actions, 
          new ParallelOptions() { MaxDegreeOfParallelism = concurrentThreadLimit}, 
          (currentAction) => 
          { 
           WaitForCooldown(threadDelay); 
           currentAction(); 
          }); 
     } 
    } 
관련 문제