问题
I've seen a number of posts about limiting the number of tasks at a time (System.Threading.Tasks - Limit the number of concurrent Tasks is a good one).
However, I need to limit the number of tasks by second - only X number of tasks per second? Is there an easy way to accomplish this?
I thought about creating a ConcurrentDictionary, the key being the current second, and the second being the count so far. The doing a check if we are at 20 for the current second, then stop. This seems suboptimal.
I'd rather do something like spin up a task every 1s/20. Any thoughts?
回答1:
I think, this can be a starting point. Below sample creates 50 Tasks (running 5 tasks/sec).
This doesn't block the code that creates the tasks. If you want to block the caller until all task have been scheduled, then you can use Task.Delay((int)shouldWait).Wait()
in QueueTask
TaskFactory taskFactory = new TaskFactory(new TimeLimitedTaskScheduler(5));
for (int i = 0; i < 50; i++)
{
var x = taskFactory.StartNew<int>(() => DateTime.Now.Second)
.ContinueWith(t => Console.WriteLine(t.Result));
}
Console.WriteLine("End of Loop");
public class TimeLimitedTaskScheduler : TaskScheduler
{
int _TaskCount = 0;
Stopwatch _Sw = null;
int _MaxTasksPerSecond;
public TimeLimitedTaskScheduler(int maxTasksPerSecond)
{
_MaxTasksPerSecond = maxTasksPerSecond;
}
protected override void QueueTask(Task task)
{
if (_TaskCount == 0) _Sw = Stopwatch.StartNew();
var shouldWait = (1000 / _MaxTasksPerSecond) * _TaskCount - _Sw.ElapsedMilliseconds;
if (shouldWait < 0)
{
shouldWait = _TaskCount = 0;
_Sw.Restart();
}
Task.Delay((int)shouldWait)
.ContinueWith(t => ThreadPool.QueueUserWorkItem((_) => base.TryExecuteTask(task)));
_TaskCount++;
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
return base.TryExecuteTask(task);
}
protected override IEnumerable<Task> GetScheduledTasks()
{
throw new NotImplementedException();
}
}
来源:https://stackoverflow.com/questions/18771524/limit-the-number-of-tasks-in-task-factory-start-by-second