Skip to main content
Code Review

Return to Question

Rollback to Revision 4
Source Link
t3chb0t
  • 44.6k
  • 9
  • 84
  • 190
public class Throttler
{
 // Use this constant as average rate to disable throttling
 public const long NoLimit = -1;
 // Number of consumed tokens
 private long _consumedTokens;
 // timestamp of last refill time
 private long _lastRefillTime;
 // ticks per period
 private long _periodTicks;
 private double _averageRate;
 public long BurstSize
 {
 get;
 set;
 }
 public long AverageRate
 {
 get { return (long)_averageRate; }
 set { _averageRate = value; }
 }
 public TimeSpan Period
 {
 get
 {
 return new TimeSpan(_periodTicks);
 }
 set
 {
 _periodTicks = value.Ticks;
 }
 }
 public Throttler()
 {
 BurstSize = 1;
 AverageRate = NoLimit;
 Period = TimeSpan.FromSeconds(1);
 }
 /// <summary>
 /// Create a Throttler
 /// ex: To throttle to 1024 byte per seconds with burst of 200 byte use
 /// new Throttler(1024,TimeSpan.FromSeconds(1), 200);
 /// </summary>
 /// <param name="averageRate">The number of tokens to add to the bucket every interval. </param>
 /// <param name="period">Timespan of on interval.</param>
 /// <param name="burstSize"></param>
 public Throttler(long averageRate, TimeSpan period, long burstSize = 1)
 {
 BurstSize = burstSize;
 AverageRate = averageRate;
 Period = period;
 }
 public longbool TryThrottledWait(long amount)
 {
 if (BurstSize <= 0 || _averageRate <= 0)
 { // Instead of throwing exception, we just let all the traffic go
 return amount;true;
 }
 RefillToken();
 return ConsumeToken(amount);
 }
 // Return number of consummed token
 private longbool ConsumeToken(long amount)
 {
 while (true)
 {
 long currentLevel = System.Threading.Volatile.Read(ref _consumedTokens);
 long available = BurstSize - currentLevel;
 if (available == 0)
 {
 return 0;
 }
 long toConsume = amount;
 currentLevel if+ (availableamount <> toConsumeBurstSize)
 {
 toConsumereturn =false; available;// not enough space for amount token
 }
 if (Interlocked.CompareExchange(ref _consumedTokens, currentLevel + toConsumeamount, currentLevel) == currentLevel)
 {
 return toConsume;true;
 }
 }
 }

 /// <summary>
 /// Wait that works inside synchronous methods. 
 /// </summary>
 /// <param name="amount">number of tokens to remove</param>
  /// <returns>Returns once all Thread.Sleep have occurred</returns>
 public void ThrottledWait(long amount)
 {
 long remaining = amount;
 while (true) {
 remaining -=if (TryThrottledWait(remainingamount);
 if (remaining == 0)
 {
 break;
 }
 long refillTime = System.Threading.Volatile.Read(ref _lastRefillTime);
 long nextRefillTime = (long) (refillTime + (_periodTicks / _averageRate));
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 long sleepTicks = Math.Max(nextRefillTime - currentTimeTicks, 0);
 TimeSpan ts = GetSleepTimenew TimeSpan(sleepTicks);
 Thread.Sleep(ts);
 }
 }
 /// <summary>
 /// Wait that works inside Async methods. 
 /// </summary>
 /// <param name="amount">number of tokens to remove</param>
 /// <returns>Returns once all Task.Delays have occurred</returns>
 public async Task ThrottledWaitAsync(long amount)
 {
 long remaining = amount;
 while (true)
 {
  remaining -= TryThrottledWait(remaining);
 if (remaining == 0)
 {
 break;
 }
 
 TimeSpan ts = GetSleepTime();
 await Task.Delay(ts).ConfigureAwait(false);
 }
 }
 /// <summary>
 /// Compute elapsed time using DateTime.UtcNow.Ticks and refil token using _periodTicks and _averageRate
 /// </summary>
 private void RefillToken()
 {
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 // Last refill time in ticks unit
 long refillTime = System.Threading.Volatile.Read(ref _lastRefillTime);
 // Time delta in ticks unit
 long TicksDelta = currentTimeTicks - refillTime;
 long newTokens = (long)(TicksDelta * _averageRate / _periodTicks);
 if (newTokens > 0)
 {
 long newRefillTime = refillTime == 0
 ? currentTimeTicks
 : refillTime + (long)(newTokens * _periodTicks / _averageRate);

 if (Interlocked.CompareExchange(ref _lastRefillTime, newRefillTime, refillTime) == refillTime)
 {
 // Loop until we succeed in refilling "newTokens" tokens
 while (true)
 {
 long currentLevel = System.Threading.Volatile.Read(ref _consumedTokens);
 long adjustedLevel = (long)Math.Min(currentLevel, BurstSize); // In case burstSize decreased
 long newLevel = (long) Math.Max(0, adjustedLevel - newTokens);
 if (Interlocked.CompareExchange(ref _consumedTokens, newLevel, currentLevel) == currentLevel)
 {
 return;
 }
 }
 }
 }
 }
 /// <summary>
 /// Get time to sleep until data can be sent again
 /// </summary>
 /// <returns>Timespan to wait</returns>
 private TimeSpan GetSleepTime()
 {
 long refillTime = Volatile.Read(ref _lastRefillTime);
 long nextRefillTime = (long)(refillTime + (_periodTicks / _averageRate));
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 long sleepTicks = Math.Max(nextRefillTime - currentTimeTicks, 0);
 TimeSpan ts = new TimeSpan(sleepTicks);
 return ts;
 }
}
public class Throttler
{
 // Use this constant as average rate to disable throttling
 public const long NoLimit = -1;
 // Number of consumed tokens
 private long _consumedTokens;
 // timestamp of last refill time
 private long _lastRefillTime;
 // ticks per period
 private long _periodTicks;
 private double _averageRate;
 public long BurstSize
 {
 get;
 set;
 }
 public long AverageRate
 {
 get { return (long)_averageRate; }
 set { _averageRate = value; }
 }
 public TimeSpan Period
 {
 get
 {
 return new TimeSpan(_periodTicks);
 }
 set
 {
 _periodTicks = value.Ticks;
 }
 }
 public Throttler()
 {
 BurstSize = 1;
 AverageRate = NoLimit;
 Period = TimeSpan.FromSeconds(1);
 }
 /// <summary>
 /// Create a Throttler
 /// ex: To throttle to 1024 byte per seconds with burst of 200 byte use
 /// new Throttler(1024,TimeSpan.FromSeconds(1), 200);
 /// </summary>
 /// <param name="averageRate">The number of tokens to add to the bucket every interval. </param>
 /// <param name="period">Timespan of on interval.</param>
 /// <param name="burstSize"></param>
 public Throttler(long averageRate, TimeSpan period, long burstSize = 1)
 {
 BurstSize = burstSize;
 AverageRate = averageRate;
 Period = period;
 }
 public long TryThrottledWait(long amount)
 {
 if (BurstSize <= 0 || _averageRate <= 0)
 { // Instead of throwing exception, we just let all the traffic go
 return amount;
 }
 RefillToken();
 return ConsumeToken(amount);
 }
 // Return number of consummed token
 private long ConsumeToken(long amount)
 {
 while (true)
 {
 long currentLevel = Volatile.Read(ref _consumedTokens);
 long available = BurstSize - currentLevel;
 if (available == 0)
 {
 return 0;
 }
 long toConsume = amount;
  if (available < toConsume)
 {
 toConsume = available;
 }
 if (Interlocked.CompareExchange(ref _consumedTokens, currentLevel + toConsume, currentLevel) == currentLevel)
 {
 return toConsume;
 }
 }
 }

 /// <summary>
 /// Wait that works inside synchronous methods. 
 /// </summary>
 /// <param name="amount">number of tokens to remove</param>
  /// <returns>Returns once all Thread.Sleep have occurred</returns>
 public void ThrottledWait(long amount)
 {
 long remaining = amount;
 while (true) {
 remaining -= TryThrottledWait(remaining);
 if (remaining == 0)
 {
 break;
 }
 TimeSpan ts = GetSleepTime();
 Thread.Sleep(ts);
 }
 }
 /// <summary>
 /// Wait that works inside Async methods. 
 /// </summary>
 /// <param name="amount">number of tokens to remove</param>
 /// <returns>Returns once all Task.Delays have occurred</returns>
 public async Task ThrottledWaitAsync(long amount)
 {
 long remaining = amount;
 while (true)
 {
  remaining -= TryThrottledWait(remaining);
 if (remaining == 0)
 {
 break;
 }
 
 TimeSpan ts = GetSleepTime();
 await Task.Delay(ts).ConfigureAwait(false);
 }
 }
 /// <summary>
 /// Compute elapsed time using DateTime.UtcNow.Ticks and refil token using _periodTicks and _averageRate
 /// </summary>
 private void RefillToken()
 {
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 // Last refill time in ticks unit
 long refillTime = Volatile.Read(ref _lastRefillTime);
 // Time delta in ticks unit
 long TicksDelta = currentTimeTicks - refillTime;
 long newTokens = (long)(TicksDelta * _averageRate / _periodTicks);
 if (newTokens > 0)
 {
 long newRefillTime = refillTime == 0
 ? currentTimeTicks
 : refillTime + (long)(newTokens * _periodTicks / _averageRate);
 if (Interlocked.CompareExchange(ref _lastRefillTime, newRefillTime, refillTime) == refillTime)
 {
 // Loop until we succeed in refilling "newTokens" tokens
 while (true)
 {
 long currentLevel = Volatile.Read(ref _consumedTokens);
 long adjustedLevel = Math.Min(currentLevel, BurstSize); // In case burstSize decreased
 long newLevel = Math.Max(0, adjustedLevel - newTokens);
 if (Interlocked.CompareExchange(ref _consumedTokens, newLevel, currentLevel) == currentLevel)
 {
 return;
 }
 }
 }
 }
 }
 /// <summary>
 /// Get time to sleep until data can be sent again
 /// </summary>
 /// <returns>Timespan to wait</returns>
 private TimeSpan GetSleepTime()
 {
 long refillTime = Volatile.Read(ref _lastRefillTime);
 long nextRefillTime = (long)(refillTime + (_periodTicks / _averageRate));
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 long sleepTicks = Math.Max(nextRefillTime - currentTimeTicks, 0);
 TimeSpan ts = new TimeSpan(sleepTicks);
 return ts;
 }
}
public class Throttler
{
 // Use this constant as average rate to disable throttling
 public const long NoLimit = -1;
 // Number of consumed tokens
 private long _consumedTokens;
 // timestamp of last refill time
 private long _lastRefillTime;
 // ticks per period
 private long _periodTicks;
 private double _averageRate;
 public long BurstSize
 {
 get;
 set;
 }
 public long AverageRate
 {
 get { return (long)_averageRate; }
 set { _averageRate = value; }
 }
 public TimeSpan Period
 {
 get
 {
 return new TimeSpan(_periodTicks);
 }
 set
 {
 _periodTicks = value.Ticks;
 }
 }
 public Throttler()
 {
 BurstSize = 1;
 AverageRate = NoLimit;
 Period = TimeSpan.FromSeconds(1);
 }
 /// <summary>
 /// Create a Throttler
 /// ex: To throttle to 1024 byte per seconds with burst of 200 byte use
 /// new Throttler(1024,TimeSpan.FromSeconds(1), 200);
 /// </summary>
 /// <param name="averageRate">The number of tokens to add to the bucket every interval. </param>
 /// <param name="period">Timespan of on interval.</param>
 /// <param name="burstSize"></param>
 public Throttler(long averageRate, TimeSpan period, long burstSize = 1)
 {
 BurstSize = burstSize;
 AverageRate = averageRate;
 Period = period;
 }
 public bool TryThrottledWait(long amount)
 {
 if (BurstSize <= 0 || _averageRate <= 0)
 { // Instead of throwing exception, we just let all the traffic go
 return true;
 }
 RefillToken();
 return ConsumeToken(amount);
 }
 private bool ConsumeToken(long amount)
 {
 while (true)
 {
 long currentLevel = System.Threading.Volatile.Read(ref _consumedTokens);
 if (currentLevel + amount > BurstSize)
 {
 return false; // not enough space for amount token
 }
 if (Interlocked.CompareExchange(ref _consumedTokens, currentLevel + amount, currentLevel) == currentLevel)
 {
 return true;
 }
 }
 }
 
 public void ThrottledWait(long amount)
 {
 while (true) {
 if (TryThrottledWait(amount))
 {
 break;
 }
 long refillTime = System.Threading.Volatile.Read(ref _lastRefillTime);
 long nextRefillTime = (long) (refillTime + (_periodTicks / _averageRate));
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 long sleepTicks = Math.Max(nextRefillTime - currentTimeTicks, 0);
 TimeSpan ts = new TimeSpan(sleepTicks);
 Thread.Sleep(ts);
 } 
 }
 /// <summary>
 /// Compute elapsed time using DateTime.UtcNow.Ticks and refil token using _periodTicks and _averageRate
 /// </summary>
 private void RefillToken()
 {
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 // Last refill time in ticks unit
 long refillTime = System.Threading.Volatile.Read(ref _lastRefillTime);
 // Time delta in ticks unit
 long TicksDelta = currentTimeTicks - refillTime;
 long newTokens = (long)(TicksDelta * _averageRate / _periodTicks);
 if (newTokens > 0)
 {
 long newRefillTime = refillTime == 0
 ? currentTimeTicks
 : refillTime + (long)(newTokens * _periodTicks / _averageRate);

 if (Interlocked.CompareExchange(ref _lastRefillTime, newRefillTime, refillTime) == refillTime)
 {
 // Loop until we succeed in refilling "newTokens" tokens
 while (true)
 {
 long currentLevel = System.Threading.Volatile.Read(ref _consumedTokens);
 long adjustedLevel = (long)Math.Min(currentLevel, BurstSize); // In case burstSize decreased
 long newLevel = (long) Math.Max(0, adjustedLevel - newTokens);
 if (Interlocked.CompareExchange(ref _consumedTokens, newLevel, currentLevel) == currentLevel)
 {
 return;
 }
 }
 }
 }
 }
}
added 1452 characters in body
Source Link
skyde
  • 193
  • 1
  • 9
public class Throttler
{
 // Use this constant as average rate to disable throttling
 public const long NoLimit = -1;
 // Number of consumed tokens
 private long _consumedTokens;
 // timestamp of last refill time
 private long _lastRefillTime;
 // ticks per period
 private long _periodTicks;
 private double _averageRate;
 public long BurstSize
 {
 get;
 set;
 }
 public long AverageRate
 {
 get { return (long)_averageRate; }
 set { _averageRate = value; }
 }
 public TimeSpan Period
 {
 get
 {
 return new TimeSpan(_periodTicks);
 }
 set
 {
 _periodTicks = value.Ticks;
 }
 }
 public Throttler()
 {
 BurstSize = 1;
 AverageRate = NoLimit;
 Period = TimeSpan.FromSeconds(1);
 }
 /// <summary>
 /// Create a Throttler
 /// ex: To throttle to 1024 byte per seconds with burst of 200 byte use
 /// new Throttler(1024,TimeSpan.FromSeconds(1), 200);
 /// </summary>
 /// <param name="averageRate">The number of tokens to add to the bucket every interval. </param>
 /// <param name="period">Timespan of on interval.</param>
 /// <param name="burstSize"></param>
 public Throttler(long averageRate, TimeSpan period, long burstSize = 1)
 {
 BurstSize = burstSize;
 AverageRate = averageRate;
 Period = period;
 }
 public boollong TryThrottledWait(long amount)
 {
 if (BurstSize <= 0 || _averageRate <= 0)
 { // Instead of throwing exception, we just let all the traffic go
 return true;amount;
 }
 RefillToken();
 return ConsumeToken(amount);
 }
 // Return number of consummed token
 private boollong ConsumeToken(long amount)
 {
 while (true)
 {
 long currentLevel = System.Threading.Volatile.Read(ref _consumedTokens);
 iflong (currentLevelavailable += amountBurstSize >- BurstSizecurrentLevel;
 if (available == 0)
 {
 return false; // not enough space for amount token0;
 }
 iflong (Interlocked.CompareExchange(reftoConsume _consumedTokens,= currentLevelamount;
 + amount, currentLevel) == currentLevel if (available < toConsume)
 {
 returntoConsume true;= available;
 }
 if (Interlocked.CompareExchange(ref _consumedTokens, currentLevel + toConsume, currentLevel) == currentLevel)
 {
 return toConsume;
 }
 }
 }

 /// <summary>
 /// Wait that works inside synchronous methods. 
 /// </summary>
 /// <param name="amount">number of tokens to remove</param>
  /// <returns>Returns once all Thread.Sleep have occurred</returns>
 public void ThrottledWait(long amount)
 {
 long remaining = amount;
 while (true) {
 ifremaining (-= TryThrottledWait(amountremaining);
 if (remaining == 0)
 {
 break;
 }
 long refillTime = System.Threading.Volatile.Read(ref _lastRefillTime);
 long nextRefillTime = (long) (refillTime + (_periodTicks / _averageRate));
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 long sleepTicks = Math.Max(nextRefillTime - currentTimeTicks, 0);
 TimeSpan ts = new TimeSpanGetSleepTime(sleepTicks);
 Thread.Sleep(ts);
 }
 }
 /// <summary>
 /// Wait that works inside Async methods. 
 /// </summary>
 /// <param name="amount">number of tokens to remove</param>
 /// <returns>Returns once all Task.Delays have occurred</returns>
 public async Task ThrottledWaitAsync(long amount)
 {
 long remaining = amount;
 while (true)
 {
  remaining -= TryThrottledWait(remaining);
 if (remaining == 0)
 {
 break;
 }
 
 TimeSpan ts = GetSleepTime();
 await Task.Delay(ts).ConfigureAwait(false);
 }
 }
 /// <summary>
 /// Compute elapsed time using DateTime.UtcNow.Ticks and refil token using _periodTicks and _averageRate
 /// </summary>
 private void RefillToken()
 {
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 // Last refill time in ticks unit
 long refillTime = System.Threading.Volatile.Read(ref _lastRefillTime);
 // Time delta in ticks unit
 long TicksDelta = currentTimeTicks - refillTime;
 long newTokens = (long)(TicksDelta * _averageRate / _periodTicks);
 if (newTokens > 0)
 {
 long newRefillTime = refillTime == 0
 ? currentTimeTicks
 : refillTime + (long)(newTokens * _periodTicks / _averageRate);

 if (Interlocked.CompareExchange(ref _lastRefillTime, newRefillTime, refillTime) == refillTime)
 {
 // Loop until we succeed in refilling "newTokens" tokens
 while (true)
 {
 long currentLevel = System.Threading.Volatile.Read(ref _consumedTokens);
 long adjustedLevel = (long)Math.Min(currentLevel, BurstSize); // In case burstSize decreased
 long newLevel = (long) Math.Max(0, adjustedLevel - newTokens);
 if (Interlocked.CompareExchange(ref _consumedTokens, newLevel, currentLevel) == currentLevel)
 {
 return;
 }
 }
 }
 }
 }
 /// <summary>
 /// Get time to sleep until data can be sent again
 /// </summary>
 /// <returns>Timespan to wait</returns>
 private TimeSpan GetSleepTime()
 {
 long refillTime = Volatile.Read(ref _lastRefillTime);
 long nextRefillTime = (long)(refillTime + (_periodTicks / _averageRate));
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 long sleepTicks = Math.Max(nextRefillTime - currentTimeTicks, 0);
 TimeSpan ts = new TimeSpan(sleepTicks);
 return ts;
 }
}
public class Throttler
{
 // Use this constant as average rate to disable throttling
 public const long NoLimit = -1;
 // Number of consumed tokens
 private long _consumedTokens;
 // timestamp of last refill time
 private long _lastRefillTime;
 // ticks per period
 private long _periodTicks;
 private double _averageRate;
 public long BurstSize
 {
 get;
 set;
 }
 public long AverageRate
 {
 get { return (long)_averageRate; }
 set { _averageRate = value; }
 }
 public TimeSpan Period
 {
 get
 {
 return new TimeSpan(_periodTicks);
 }
 set
 {
 _periodTicks = value.Ticks;
 }
 }
 public Throttler()
 {
 BurstSize = 1;
 AverageRate = NoLimit;
 Period = TimeSpan.FromSeconds(1);
 }
 /// <summary>
 /// Create a Throttler
 /// ex: To throttle to 1024 byte per seconds with burst of 200 byte use
 /// new Throttler(1024,TimeSpan.FromSeconds(1), 200);
 /// </summary>
 /// <param name="averageRate">The number of tokens to add to the bucket every interval. </param>
 /// <param name="period">Timespan of on interval.</param>
 /// <param name="burstSize"></param>
 public Throttler(long averageRate, TimeSpan period, long burstSize = 1)
 {
 BurstSize = burstSize;
 AverageRate = averageRate;
 Period = period;
 }
 public bool TryThrottledWait(long amount)
 {
 if (BurstSize <= 0 || _averageRate <= 0)
 { // Instead of throwing exception, we just let all the traffic go
 return true;
 }
 RefillToken();
 return ConsumeToken(amount);
 }
 private bool ConsumeToken(long amount)
 {
 while (true)
 {
 long currentLevel = System.Threading.Volatile.Read(ref _consumedTokens);
 if (currentLevel + amount > BurstSize)
 {
 return false; // not enough space for amount token
 }
 if (Interlocked.CompareExchange(ref _consumedTokens, currentLevel + amount, currentLevel) == currentLevel)
 {
 return true;
 }
 }
 }
 
 public void ThrottledWait(long amount)
 {
 while (true) {
 if (TryThrottledWait(amount))
 {
 break;
 }
 long refillTime = System.Threading.Volatile.Read(ref _lastRefillTime);
 long nextRefillTime = (long) (refillTime + (_periodTicks / _averageRate));
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 long sleepTicks = Math.Max(nextRefillTime - currentTimeTicks, 0);
 TimeSpan ts = new TimeSpan(sleepTicks);
 Thread.Sleep(ts);
 } 
 }
 /// <summary>
 /// Compute elapsed time using DateTime.UtcNow.Ticks and refil token using _periodTicks and _averageRate
 /// </summary>
 private void RefillToken()
 {
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 // Last refill time in ticks unit
 long refillTime = System.Threading.Volatile.Read(ref _lastRefillTime);
 // Time delta in ticks unit
 long TicksDelta = currentTimeTicks - refillTime;
 long newTokens = (long)(TicksDelta * _averageRate / _periodTicks);
 if (newTokens > 0)
 {
 long newRefillTime = refillTime == 0
 ? currentTimeTicks
 : refillTime + (long)(newTokens * _periodTicks / _averageRate);

 if (Interlocked.CompareExchange(ref _lastRefillTime, newRefillTime, refillTime) == refillTime)
 {
 // Loop until we succeed in refilling "newTokens" tokens
 while (true)
 {
 long currentLevel = System.Threading.Volatile.Read(ref _consumedTokens);
 long adjustedLevel = (long)Math.Min(currentLevel, BurstSize); // In case burstSize decreased
 long newLevel = (long) Math.Max(0, adjustedLevel - newTokens);
 if (Interlocked.CompareExchange(ref _consumedTokens, newLevel, currentLevel) == currentLevel)
 {
 return;
 }
 }
 }
 }
 }
}
public class Throttler
{
 // Use this constant as average rate to disable throttling
 public const long NoLimit = -1;
 // Number of consumed tokens
 private long _consumedTokens;
 // timestamp of last refill time
 private long _lastRefillTime;
 // ticks per period
 private long _periodTicks;
 private double _averageRate;
 public long BurstSize
 {
 get;
 set;
 }
 public long AverageRate
 {
 get { return (long)_averageRate; }
 set { _averageRate = value; }
 }
 public TimeSpan Period
 {
 get
 {
 return new TimeSpan(_periodTicks);
 }
 set
 {
 _periodTicks = value.Ticks;
 }
 }
 public Throttler()
 {
 BurstSize = 1;
 AverageRate = NoLimit;
 Period = TimeSpan.FromSeconds(1);
 }
 /// <summary>
 /// Create a Throttler
 /// ex: To throttle to 1024 byte per seconds with burst of 200 byte use
 /// new Throttler(1024,TimeSpan.FromSeconds(1), 200);
 /// </summary>
 /// <param name="averageRate">The number of tokens to add to the bucket every interval. </param>
 /// <param name="period">Timespan of on interval.</param>
 /// <param name="burstSize"></param>
 public Throttler(long averageRate, TimeSpan period, long burstSize = 1)
 {
 BurstSize = burstSize;
 AverageRate = averageRate;
 Period = period;
 }
 public long TryThrottledWait(long amount)
 {
 if (BurstSize <= 0 || _averageRate <= 0)
 { // Instead of throwing exception, we just let all the traffic go
 return amount;
 }
 RefillToken();
 return ConsumeToken(amount);
 }
 // Return number of consummed token
 private long ConsumeToken(long amount)
 {
 while (true)
 {
 long currentLevel = Volatile.Read(ref _consumedTokens);
 long available = BurstSize - currentLevel;
 if (available == 0)
 {
 return 0;
 }
 long toConsume = amount;
  if (available < toConsume)
 {
 toConsume = available;
 }
 if (Interlocked.CompareExchange(ref _consumedTokens, currentLevel + toConsume, currentLevel) == currentLevel)
 {
 return toConsume;
 }
 }
 }

 /// <summary>
 /// Wait that works inside synchronous methods. 
 /// </summary>
 /// <param name="amount">number of tokens to remove</param>
  /// <returns>Returns once all Thread.Sleep have occurred</returns>
 public void ThrottledWait(long amount)
 {
 long remaining = amount;
 while (true) {
 remaining -= TryThrottledWait(remaining);
 if (remaining == 0)
 {
 break;
 }
 TimeSpan ts = GetSleepTime();
 Thread.Sleep(ts);
 }
 }
 /// <summary>
 /// Wait that works inside Async methods. 
 /// </summary>
 /// <param name="amount">number of tokens to remove</param>
 /// <returns>Returns once all Task.Delays have occurred</returns>
 public async Task ThrottledWaitAsync(long amount)
 {
 long remaining = amount;
 while (true)
 {
  remaining -= TryThrottledWait(remaining);
 if (remaining == 0)
 {
 break;
 }
 
 TimeSpan ts = GetSleepTime();
 await Task.Delay(ts).ConfigureAwait(false);
 }
 }
 /// <summary>
 /// Compute elapsed time using DateTime.UtcNow.Ticks and refil token using _periodTicks and _averageRate
 /// </summary>
 private void RefillToken()
 {
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 // Last refill time in ticks unit
 long refillTime = Volatile.Read(ref _lastRefillTime);
 // Time delta in ticks unit
 long TicksDelta = currentTimeTicks - refillTime;
 long newTokens = (long)(TicksDelta * _averageRate / _periodTicks);
 if (newTokens > 0)
 {
 long newRefillTime = refillTime == 0
 ? currentTimeTicks
 : refillTime + (long)(newTokens * _periodTicks / _averageRate);
 if (Interlocked.CompareExchange(ref _lastRefillTime, newRefillTime, refillTime) == refillTime)
 {
 // Loop until we succeed in refilling "newTokens" tokens
 while (true)
 {
 long currentLevel = Volatile.Read(ref _consumedTokens);
 long adjustedLevel = Math.Min(currentLevel, BurstSize); // In case burstSize decreased
 long newLevel = Math.Max(0, adjustedLevel - newTokens);
 if (Interlocked.CompareExchange(ref _consumedTokens, newLevel, currentLevel) == currentLevel)
 {
 return;
 }
 }
 }
 }
 }
 /// <summary>
 /// Get time to sleep until data can be sent again
 /// </summary>
 /// <returns>Timespan to wait</returns>
 private TimeSpan GetSleepTime()
 {
 long refillTime = Volatile.Read(ref _lastRefillTime);
 long nextRefillTime = (long)(refillTime + (_periodTicks / _averageRate));
 long currentTimeTicks = DateTime.UtcNow.Ticks;
 long sleepTicks = Math.Max(nextRefillTime - currentTimeTicks, 0);
 TimeSpan ts = new TimeSpan(sleepTicks);
 return ts;
 }
}
Notice removed Canonical answer required by Community Bot
Bounty Ended with Peter Taylor's answer chosen by Community Bot
added 332 characters in body
Source Link
skyde
  • 193
  • 1
  • 9

To throttle to 1024 byte per seconds with burst of 200 byte we would do

var throttler = new Throttler(1024,TimeSpan.FromSeconds(1), 200);

Then each time we need to send some byte

void Sendbytes(byte[] byteArray) {
 throttler.ThrottledWait(byteArray.Length);
 ...
 // write the bytes
}

To throttle to 1024 byte per seconds with burst of 200 byte we would do

var throttler = new Throttler(1024,TimeSpan.FromSeconds(1), 200);

Then each time we need to send some byte

void Sendbytes(byte[] byteArray) {
 throttler.ThrottledWait(byteArray.Length);
 ...
 // write the bytes
}
Tweeted twitter.com/StackCodeReview/status/841743024820494336
Notice added Canonical answer required by skyde
Bounty Started worth 100 reputation by skyde
Post Reopened by forsvarir, alecxe, janos
added 5 characters in body
Source Link
janos
  • 112.9k
  • 15
  • 154
  • 396
Loading
added 625 characters in body
Source Link
skyde
  • 193
  • 1
  • 9
Loading
Post Closed as "Needs details or clarity" by Bruno Costa, alecxe, forsvarir, mdfst13, janos
Source Link
skyde
  • 193
  • 1
  • 9
Loading
lang-cs

AltStyle によって変換されたページ (->オリジナル) /