我遇到这种情况:
var tasks = new List<ITask> ...
Parallel.ForEach(tasks, currentTask => currentTask.Execute() );
是否有可能指示PLinq在下一个线程产生之前等待500ms?
System.Threading.Thread.Sleep(5000);
答案 0 :(得分:6)
您使用Parallel.Foreach
完全错误,您应该制作一个特殊的枚举器,其速率限制自己每500毫秒获取一次数据。
由于您未提供任何详细信息,我对DTO的工作方式做了一些假设。
private IEnumerator<SomeResource> GetRateLimitedResource()
{
SomeResource someResource = null;
do
{
someResource = _remoteProvider.GetData();
if(someResource != null)
{
yield return someResource;
Thread.Sleep(500);
}
} while (someResource != null);
}
这是你的并列应该如何看待
Parallel.ForEach(GetRateLimitedResource(), SomeFunctionToProcessSomeResource);
答案 1 :(得分:3)
已经有一些很好的建议了。我同意其他人的意见,即你正在以不应该使用的方式使用PLINQ。
我的建议是使用System.Threading.Timer。这可能比编写一个返回强制半秒延迟的IEnumerable<>
的方法更好,因为您可能不需要等待整整半秒,这取决于自上次API调用以来经过了多长时间。
使用计时器,它将调用你在指定的时间间隔提供它的委托,所以即使第一个任务没有完成,半秒后它会在另一个线程上调用你的委托,所以不会有任何额外的等待。
从您的示例代码中,听起来您有一个任务列表,在这种情况下,我会使用System.Collections.Concurrent.ConcurrentQueue来跟踪任务。队列为空后,关闭计时器。
答案 2 :(得分:2)
您可以改用Enumerable.Aggregate
。
var task = tasks.Aggregate((t1, t2) =>
t1.ContinueWith(async _ =>
{ Thread.Sleep(500); return t2.Result; }));
如果您不希望链接的任务,那么Select
也会发生重载,假设任务是按照延迟的顺序。
var tasks = Enumerable
.Range(1, 10)
.Select(x => Task.Run(() => x * 2))
.Select((x, i) => Task.Delay(TimeSpan.FromMilliseconds(i * 500))
.ContinueWith(_ => x.Result));
foreach(var result in tasks.Select(x => x.Result))
{
Console.WriteLine(result);
}
从评论中,更好的选择是保护资源而不是使用时间延迟。
static object Locker = new object();
static int GetResultFromResource(int arg)
{
lock(Locker)
{
Thread.Sleep(500);
return arg * 2;
}
}
var tasks = Enumerable
.Range(1, 10)
.Select(x => Task.Run(() => GetResultFromResource(x)));
foreach(var result in tasks.Select(x => x.Result))
{
Console.WriteLine(result);
}
答案 3 :(得分:1)
在这种情况下,具有BlockingCollection<T>
的生产者 - 消费者模式怎么样?
var tasks = new BlockingCollection<ITask>();
// add tasks, if this is an expensive process, put it out onto a Task
// tasks.Add(x);
// we're done producin' (allows GetConsumingEnumerable to finish)
tasks.CompleteAdding();
RunTasks(tasks);
使用单个消费者线程:
static void RunTasks(BlockingCollection<ITask> tasks)
{
foreach (var task in tasks.GetConsumingEnumerable())
{
task.Execute();
// this may not be as accurate as you would like
Thread.Sleep(500);
}
}
如果您有权访问.Net 4.5,则可以使用Task.Delay
:
static void RunTasks(BlockingCollection<ITask> tasks)
{
foreach (var task in tasks.GetConsumingEnumerable())
{
Task.Delay(500)
.ContinueWith(() => task.Execute())
.Wait();
}
}