// Original source from: http://codereview.stackexchange.com/questions/11377/implementation-of-a-throttled-concurrentqueue-rx-observer public static IObservable <T> AsRateLimitedObservable <T>(this BlockingCollection <T> sequence, int occurrences, TimeSpan timeUnit, CancellationToken cancellationToken) { var subject = new Subject <T>(); var token = new CancellationToken(); var tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, token); var consumingTask = new Task(() => { using (var throttle = new Throttle(occurrences, timeUnit)) { while (!sequence.IsCompleted) { try { var item = sequence.Take(cancellationToken); throttle.WaitToProceed(); try { subject.OnNext(item); } catch (Exception ex) { subject.OnError(ex); } } catch (OperationCanceledException) { break; } } subject.OnCompleted(); } }, TaskCreationOptions.LongRunning); return(new TaskAwareObservable <T>(subject, consumingTask, tokenSource)); }
// TODO: devise a way to avoid problems if collection gets too big (produced faster than consumed) public static IObservable <T> AsRateLimitedObservable <T>(this BlockingCollection <T> sequence, int items, TimeSpan timePeriod, CancellationToken producerToken) { Subject <T> subject = new Subject <T>(); // this is a dummyToken just so we can recreate the TokenSource // which we will pass the proxy class so it can cancel the task // on disposal CancellationToken dummyToken = new CancellationToken(); CancellationTokenSource tokenSource = CancellationTokenSource.CreateLinkedTokenSource(producerToken, dummyToken); var consumingTask = new Task(() => { using (var throttle = new Throttle(items, timePeriod)) { while (!sequence.IsCompleted) { try { T item = sequence.Take(producerToken); throttle.WaitToProceed(); try { subject.OnNext(item); } catch (Exception ex) { subject.OnError(ex); } } catch (OperationCanceledException) { break; } } subject.OnCompleted(); } }, TaskCreationOptions.LongRunning); return(new TaskAwareObservable <T>(subject, consumingTask, tokenSource)); }