基本上我试图能够限制列表迭代的执行速度。
我非常喜欢使用RX的想法,因为我可以构建它的顶部,并且有一个更优雅的解决方案,但它不必使用RX完成。
我已经在比我更聪明的人的帮助下制定了这个。我的问题是我希望能够说一些收集.RateLimitedForEach(比率,功能),并让它最终阻止直到我们已经完成了处理......或者让它成为异步方法。
该功能下方的演示在控制台应用程序中运行,但如果我在foreach之后关闭,它会立即返回。
我有点不知道这是否可以解决,或者我是否应该完全不同
public static void RateLimitedForEach<T>(this List<T> list, double minumumDelay, Action<T> action)
{
list.ToObservable().Zip(Observable.Interval(TimeSpan.FromSeconds(minumumDelay)), (v, _) => v)
.Do(action).Subscribe();
}
//rate limits iteration of foreach... keep in mind this is not the same thing as just sleeping for a second
//between each iteration, this is saying at the start of the next iteration, if minimum delay time hasnt past, hold until it has
var maxRequestsPerMinute = 60;
requests.RateLimitedForeach(60/maxRequestsPerMinute,(request) => SendRequest(request));
答案 0 :(得分:4)
但不一定要使用RX
以下是如何同步执行此操作:
public static void RateLimitedForEach<T>(
this List<T> list,
double minumumDelay,
Action<T> action)
{
foreach (var item in list)
{
Stopwatch sw = Stopwatch.StartNew();
action(item);
double left = minumumDelay - sw.Elapsed.TotalSeconds;
if(left > 0)
Thread.Sleep(TimeSpan.FromSeconds(left));
}
}
以下是如何异步执行此操作(只有潜在的等待是异步的):
public static async Task RateLimitedForEachAsync<T>(
this List<T> list,
double minumumDelay,
Action<T> action)
{
foreach (var item in list)
{
Stopwatch sw = Stopwatch.StartNew();
action(item);
double left = minumumDelay - sw.Elapsed.TotalSeconds;
if (left > 0)
await Task.Delay(TimeSpan.FromSeconds(left));
}
}
请注意,您可以更改异步版本,使其自动异步,如下所示:
public static async Task RateLimitedForEachAsync<T>(
this List<T> list,
double minumumDelay,
Func<T,Task> async_task_func)
{
foreach (var item in list)
{
Stopwatch sw = Stopwatch.StartNew();
await async_task_func(item);
double left = minumumDelay - sw.Elapsed.TotalSeconds;
if (left > 0)
await Task.Delay(TimeSpan.FromSeconds(left));
}
}
如果您需要在每个项目上运行的操作是异步的,这将非常有用。
最后一个版本可以像这样使用:
List<string> list = new List<string>();
list.Add("1");
list.Add("2");
var task = list.RateLimitedForEachAsync(1.0, async str =>
{
//Do something asynchronous here, e.g.:
await Task.Delay(500);
Console.WriteLine(DateTime.Now + ": " + str);
});
现在你应该等task
完成。如果这是Main
方法,那么您需要同步等待:
task.Wait();
另一方面,如果你在异步方法中,那么你需要像这样异步等待:
await task;
答案 1 :(得分:0)
您需要获得的概念是主线程没有等待您的RateLimitedForEach
调用完成。此外 - 在您的控制台应用程序上 - 主线程结束后,流程结束。
这是什么意思?这意味着无论RateLimitedForEach
上的观察者是否已完成执行,该过程都将结束。
注意:用户可能仍然强制执行您的应用程序,这是一件好事。如果您希望能够在不挂起UI的情况下等待,则可以使用表单应用程序,如果您不希望用户关闭与该过程相关的窗口,则可以使用服务。
使用任务是我在下面提供的superios solution。
请注意,在控制台应用程序上使用“任务”时,您仍需要等待任务以防止主线程在RateLimitedForEach
完成其作业之前完成。仍然建议远离控制台应用程序。
如果您坚持继续使用您的代码,您可以调整它以挂起调用线程直到完成:
public static void RateLimitedForEach<T>
(
this List<T> list,
double minumumDelay,
Action<T> action
)
{
using (var waitHandle = new ManualResetEventSlim(false))
{
var mainObservable = list.ToObservable();
var intervalObservable = Observable.Interval(TimeSpan.FromSeconds(minumumDelay));
var zipObservable = mainObservable .Zip(intervalObservable, (v, _) => v);
zipObservable.Subscribe
(
action,
error => GC.KeepAlive(error), // Ingoring them, as you already were
() => waitHandle.Set() // <-- "Done signal"
);
waitHandle.Wait(); // <--- Waiting on the observer to complete
}
}
答案 2 :(得分:0)
您的代码非常完美。
请改为尝试:
public static void RateLimitedForEach<T>(this List<T> list, double minumumDelay, Action<T> action)
{
list
.ToObservable()
.Zip(Observable.Interval(TimeSpan.FromSeconds(minumumDelay)), (v, _) => v)
.Do(action)
.ToArray()
.Wait();
}
答案 3 :(得分:-1)
RX Throttle不能做你想要的吗?
https://msdn.microsoft.com/en-us/library/hh229400(v=vs.103).aspx