C# 并行ForEach在产卵前等待500毫秒

C# 并行ForEach在产卵前等待500毫秒,c#,.net,plinq,C#,.net,Plinq,我有这样的情况: var tasks = new List<ITask> ... Parallel.ForEach(tasks, currentTask => currentTask.Execute() ); 你可以用它来代替 如果您不想将任务链接起来,那么假设任务是按延迟顺序进行的,也会有过载 var tasks = Enumerable .Range(1, 10) .Select(x => Task.Run((

我有这样的情况:

var tasks = new List<ITask> ...
Parallel.ForEach(tasks, currentTask => currentTask.Execute() );
你可以用它来代替

如果您不想将任务链接起来,那么假设任务是按延迟顺序进行的,也会有过载

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);
}
你可以用它来代替

如果您不想将任务链接起来,那么假设任务是按延迟顺序进行的,也会有过载

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);
}

您使用的是
Parallel。Foreach
完全错误,您应该使用一个特殊的枚举器,该枚举器的速率限制为每500毫秒获取一次数据

由于你没有提供任何细节,我对你的工作方式做了一些假设

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
完全错误,您应该使用一个特殊的枚举器,该枚举器的速率限制为每500毫秒获取一次数据

由于你没有提供任何细节,我对你的工作方式做了一些假设

private IEnumerator<SomeResource> GetRateLimitedResource()
{
    SomeResource someResource = null;
    do
    {
        someResource = _remoteProvider.GetData();

        if(someResource != null)
        {
             yield return someResource;
             Thread.Sleep(500);
        }
    } while (someResource != null);
}

在这种情况下,生产者-消费者模式如何


在这种情况下,生产者-消费者模式如何


已经有一些好的建议了。我同意其他人的看法,即您使用PLINQ的方式并不是为了使用它

我的建议是使用。这可能比编写一个返回强制半秒延迟的
IEnumerable
的方法要好,因为您可能不需要等待半秒,这取决于自上次API调用以来经过的时间

使用计时器,它将在指定的时间间隔调用您提供给它的委托,因此即使第一个任务没有完成,半秒钟后它也会在另一个线程上调用您的委托,因此不会有任何额外的等待


从您的示例代码中,听起来您有一个任务列表,在本例中,我将使用它来跟踪任务。队列清空后,关闭计时器。

已经有一些好的建议。我同意其他人的看法,即您使用PLINQ的方式并不是为了使用它

我的建议是使用。这可能比编写一个返回强制半秒延迟的
IEnumerable
的方法要好,因为您可能不需要等待半秒,这取决于自上次API调用以来经过的时间

使用计时器,它将在指定的时间间隔调用您提供给它的委托,因此即使第一个任务没有完成,半秒钟后它也会在另一个线程上调用您的委托,因此不会有任何额外的等待



从您的示例代码中,听起来您有一个任务列表,在本例中,我将使用它来跟踪任务。一旦队列为空,请关闭计时器。

您想在这里实现什么?我假设
Parallel.ForEach(tasks,currentTask=>{Thread.Sleep(5000);currentTask.Execute();})
可以做到这一点,但我想知道您为什么要这样做——听起来像是某种解决方法?每个任务都需要一段时间才能执行,它的数据来自另一个资源,只能每0.5秒命中一次。我想我可以将获取数据和执行任务分开…不,这可能会在任何时候失败,不要假设它会在500毫秒内完成,你可以使用
WaitHandles
这里分离将是第一选择。你在这里试图实现什么?我会假设
Parallel.ForEach(任务,currentTask=>{Thread.Sleep(5000);currentTask.Execute();})
可以做到这一点,但我想知道您为什么要这样做——听起来像是某种解决方法?每个任务都需要一段时间才能执行,它的数据来自另一个资源,只能每0.5秒命中一次。我想我可以分开获取数据和执行任务…不,这可能会在任何时候失败,不要假设它会在500毫秒内完成,你可以在这里使用
WaitHandles
,分离是这里的第一选择。某种程度上回答了问题,但删除了所有并发。我添加了另一个将并发运行的方法。某种程度上回答了问题,但删除了所有并发。我添加了另一个将并发运行的方法。这个主意不错,因为它很简单强大。这将等待500毫秒,然后检索第一个项目。这是故意的吗?@svick我不得不把睡眠放在某个地方,我在抓取数据后移动到了,这样它就可以睡觉了。通过获取
日期时间,可以进一步改进代码。现在
并将其保存在局部变量中,然后在下一次迭代时,您可以检查500毫秒是否已经过去,并且只睡眠所需的时间。这可能无法按预期工作,因为AFAIK默认情况下,
Parallel.ForEach
使用块分区。这意味着一次将逐步枚举越来越多的元素。当需要减少同步开销时,块划分是有意义的,但在这种情况下,开销与所施加的人为延迟相比完全相形见绌。因此,禁用它是有意义的,方法是将
GetRateLimitedResource()
替换为
Partitioner.Create(GetRateLimitedResource(),EnumerablePartitionOptions.NoBuffering)
。这是一个好主意,因为它简单但功能强大。在检索第一个项目之前需要等待500毫秒。这是故意的吗?@svick我不得不把睡眠放在某个地方,我在抓取数据后移动到了,这样它就可以睡觉了。通过获取
日期时间,可以进一步改进代码。现在
并将其保存在局部变量中,然后在下一次迭代时,您可以检查500毫秒是否已经过去,并且只睡眠所需的时间。这可能无法按预期工作,因为AFAIK默认情况下,
Parallel.ForEach
使用块分区。意思是逐渐地将
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);
    }
}
static void RunTasks(BlockingCollection<ITask> tasks)
{
    foreach (var task in tasks.GetConsumingEnumerable())
    {
        Task.Delay(500)
            .ContinueWith(() => task.Execute())
            .Wait();
    }
}