C# 如何在第三方物流中使用Parallel而不是While

C# 如何在第三方物流中使用Parallel而不是While,c#,.net,multithreading,task-parallel-library,parallel-for,C#,.net,Multithreading,Task Parallel Library,Parallel For,我想在语句中使用parallel for,而不是while语句。当我查看只有已知计数或变量计数的并行运行示例时 但我不知道我的循环将运行多少次,并且它无法在运行时将其链接到变量 我将尝试使用TPL和经典代码进行简单的性能测试。所以我写了一个模类,用减量运算计算模。我的功能是 long FindModulus(long n, int i) { while ( n >= i ) n -= i; return n; } 我的目标是用并行For循环替换这个循环 我

我想在语句中使用parallel for,而不是while语句。当我查看只有已知计数或变量计数的并行运行示例时

但我不知道我的循环将运行多少次,并且它无法在运行时将其链接到变量

我将尝试使用TPL和经典代码进行简单的性能测试。所以我写了一个模类,用减量运算计算模。我的功能是

long FindModulus(long n, int i)
{
    while ( n >= i )
       n -= i;
    return  n;
}
我的目标是用并行For循环替换这个循环

我还想学习我能不能用一个if和break语句来并行

我想我需要一个锁,因为n的值在所有线程中都会改变,任何代码示例都将受到欢迎


如果您不知道循环将并行通过多少次,请提前感谢。For不是选项。但您可以轻松地使用简单的任务并自己完成:

object syncRoot = new object();
bool finished = false;
private bool Finished()
{
    // or implement any other logic to evaluate whether loop has finished
    // but thread safe
    lock (this.syncRoot)
    {
        return this.finished;
    }
} 

...

List<Task> tasks = new List<Task>();
while (this.Finished())
{
    var task = new Task(() =>
    {
        // implement your loop logic here
    })
    task.Start();
    tasks.Add(task);
}

Task.WaitAll(tasks);

如果您不知道循环将并行通过多少次。For不是一个选项。但您可以轻松地使用简单的任务并自己完成:

object syncRoot = new object();
bool finished = false;
private bool Finished()
{
    // or implement any other logic to evaluate whether loop has finished
    // but thread safe
    lock (this.syncRoot)
    {
        return this.finished;
    }
} 

...

List<Task> tasks = new List<Task>();
while (this.Finished())
{
    var task = new Task(() =>
    {
        // implement your loop logic here
    })
    task.Start();
    tasks.Add(task);
}

Task.WaitAll(tasks);
我会像下面那样编写我的并行类

public static class MyParallel
{
    public static void While(Func<bool> condition, Action action)
    {
        Parallel.ForEach(WhileTrue(condition), _ => action());
    }

    static IEnumerable<bool> WhileTrue(Func<bool> condition)
    {
        while (condition()) yield return true;
    }
}
像这样使用它

int i=0;
MyParallel.While( 
    () => {
        lock (SomeLockObject)
        {
            return i++<10;
        }
    },  
    () => Console.WriteLine("test")
);
别忘了锁定共享对象如果您修改了在条件/操作中使用的对象,我会像下面那样编写我的并行类

public static class MyParallel
{
    public static void While(Func<bool> condition, Action action)
    {
        Parallel.ForEach(WhileTrue(condition), _ => action());
    }

    static IEnumerable<bool> WhileTrue(Func<bool> condition)
    {
        while (condition()) yield return true;
    }
}
像这样使用它

int i=0;
MyParallel.While( 
    () => {
        lock (SomeLockObject)
        {
            return i++<10;
        }
    },  
    () => Console.WriteLine("test")
);

如果您修改了在条件/操作中使用的共享对象,请不要忘记锁定它们。例如,由于无法接收引用变量或Func,因此我们仅限于使用良好的ol任务。下面是一个例子:

int n = 100;
int i = 3;
int accum = 0;
object logicLock = new object();
Random rand = new Random();

void Main()
{
    // No point of having more tasks than available cores.
    int maxTasks = 4;
    Task[] tasks = new Task[maxTasks];
    int count = 0;
    while(this.CheckCondition())
    {
        int index = count;
        if(count++ >= maxTasks)
        {
            Console.WriteLine("Waiting for a task slot");
            index = Task.WaitAny(tasks);
        }

        Console.WriteLine("Executing a task in slot: {0}", index);
        tasks[index] = Task.Factory.StartNew(LoopLogic);
    }

    Console.WriteLine("Done");
}

public void LoopLogic()
{
    lock(logicLock)
    {
        accum += i;
    }

    int sleepTime = rand.Next(0, 500);
    Thread.Sleep(sleepTime);
}

public bool CheckCondition()
{
    lock(logicLock) 
    {
        return (n - accum) >= i;
    }
}
结果:

正在插槽0中执行任务 在插槽1中执行任务 在插槽2中执行任务 在插槽3中执行任务 正在等待任务槽 在插槽2中执行任务 正在等待任务槽 在插槽1中执行任务 正在等待任务槽 在插槽3中执行任务 正在等待任务槽 在插槽1中执行任务 正在等待任务槽 正在插槽0中执行任务 正在等待任务槽 在插槽3中执行任务 正在等待任务槽 在插槽2中执行任务 ... 更多的是相同的。
Done

并行。例如,For无法接收参考变量或Func,因此我们仅限于使用良好的ol'任务。下面是一个例子:

int n = 100;
int i = 3;
int accum = 0;
object logicLock = new object();
Random rand = new Random();

void Main()
{
    // No point of having more tasks than available cores.
    int maxTasks = 4;
    Task[] tasks = new Task[maxTasks];
    int count = 0;
    while(this.CheckCondition())
    {
        int index = count;
        if(count++ >= maxTasks)
        {
            Console.WriteLine("Waiting for a task slot");
            index = Task.WaitAny(tasks);
        }

        Console.WriteLine("Executing a task in slot: {0}", index);
        tasks[index] = Task.Factory.StartNew(LoopLogic);
    }

    Console.WriteLine("Done");
}

public void LoopLogic()
{
    lock(logicLock)
    {
        accum += i;
    }

    int sleepTime = rand.Next(0, 500);
    Thread.Sleep(sleepTime);
}

public bool CheckCondition()
{
    lock(logicLock) 
    {
        return (n - accum) >= i;
    }
}
结果:

正在插槽0中执行任务 在插槽1中执行任务 在插槽2中执行任务 在插槽3中执行任务 正在等待任务槽 在插槽2中执行任务 正在等待任务槽 在插槽1中执行任务 正在等待任务槽 在插槽3中执行任务 正在等待任务槽 在插槽1中执行任务 正在等待任务槽 正在插槽0中执行任务 正在等待任务槽 在插槽3中执行任务 正在等待任务槽 在插槽2中执行任务 ... 更多的是相同的。
完成

因为你没有在循环中做任何工作,所以任何例子都是虚构的。但是,如果您坚持,下面的示例传达了这样一个想法:它将比同步版本慢,因为同步开销大于工作本身:

long _n;
int _i;
long _mod;

long FindModulusParallel(long n, int i)
{
    _mod = _n = n;
    _i = i;

    var actions = Enumerable.Range(0, Environment.ProcessorCount)
                            .Select<int,Action>(j => Subtract).ToArray();
    Parallel.Invoke(actions);

    return _mod;
}

void Subtract()
{
    while (Interlocked.Add(ref _n, -_i) >= 0)
        Interlocked.Add(ref _mod, -_i);
}

因为你没有在循环中做任何工作,所以任何例子都是虚构的。但是,如果您坚持,下面的示例传达了这样一个想法:它将比同步版本慢,因为同步开销大于工作本身:

long _n;
int _i;
long _mod;

long FindModulusParallel(long n, int i)
{
    _mod = _n = n;
    _i = i;

    var actions = Enumerable.Range(0, Environment.ProcessorCount)
                            .Select<int,Action>(j => Subtract).ToArray();
    Parallel.Invoke(actions);

    return _mod;
}

void Subtract()
{
    while (Interlocked.Add(ref _n, -_i) >= 0)
        Interlocked.Add(ref _mod, -_i);
}

你能发布一个更好的例子吗?添加了更多的定义,抱歉延迟,我遇到了网络问题。它仍然不太现实,Foo不需要任何循环或锁定。我更改了函数名并添加了锁定原因TPL将产生比这个例子合理的更多的开销。你能发布一个更好的例子吗?添加了更多的定义,抱歉延迟,我有网络问题它仍然不太现实,Foo不需要任何循环或锁定。我更改了函数名并添加了锁推理TPL将产生比本例合理的更多的开销。并行类和任务有所不同。前者确保在可用处理器单元上有效执行,后者确保在特定任务调度上有效执行并行类和任务是不同的。第一种方法确保在可用处理器单元上有效执行,第二种方法确保在特定任务调度程序上有效执行不应使用static累加,然后在代码中添加超过10个staticaddition,或者忽略了一个更简单的更新?我在该代码示例中使用了LINQPad。LINQPad不要求您定义静态、字符串args[]等。Google:LINQPad不应该与static一起累加,然后在整个代码中添加超过10个staticadditions,或者有一个我忽略的更简单的更新?我用LINQPad作为代码示例。LINQPad不要求您定义静态、字符串args[]等。谷歌:LINQPad