如何在 TPL 中使用 Parallel For 代替 While

发布于 2025-01-05 11:46:53 字数 444 浏览 7 评论 0原文

我想在语句中使用并行 for 而不是 while 语句。当我查看仅具有已知或变量计数的样本 Parallel For 运行时。

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

我将尝试使用 TPL 和经典代码进行简单的性能测试。所以我正在编写一个模数类,它通过减量运算来计算模数。我的函数就像

long FindModulus(long n, int i)
{
    while ( n >= i )
       n -= i;
    return  n;
}

我的目标是用 Parallel For 循环替换这个循环

,我还想了解我可以将 Parallel For 与 if 和 break 语句一起使用。

我想我需要一个锁,因为 n 的值将在所有线程中更改,任何代码示例将不胜感激,

提前致谢

I want to use parallel for in a statement instead of while statement. When i look at samples Parallel For runs with only a known -or variable- count.

But i don't know how many times my loop will run, and its unable to link it to a variable in run time.

I'm gonna try a simple performance test with TPL and classic code. So i'm writing a modulus class which calculates modulus with decrement operation. My function is like

long FindModulus(long n, int i)
{
    while ( n >= i )
       n -= i;
    return  n;
}

My goal is replacing this loop with a Parallel For loop

and i also want to learn can i use Parallel For with an if and break statement.

I think i will need a lock because value of n will be changed in all threads, any code sample would be appreciated

Thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

喜你已久 2025-01-12 11:46:53

如果您不知道循环将传递多少次,则 Parallel.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);

if you don't know how many times the loop will pass Parallel.For is not an option. But you can easily use simple tasks and do it for yourself:

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);
烦人精 2025-01-12 11:46:53

我会像下面这样编写 MyParallel 类

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

不要忘记锁定 condition/action 中使用的共享对象(如果您修改它们)

I would write MyParallel class like below

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

and use it like this.

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

Don't forget to lock the shared objects(if you modify them) used in condition/action

嗫嚅 2025-01-12 11:46:53

由于您没有在循环内做任何工作,因此任何示例都会被设计。但如果你坚持的话,这里有一个传达这个想法的例子(它会比同步版本慢,因为同步开销比工作本身更大):

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

Since you're not doing any work inside the loop any example would be contrived. But if you insist, here is an example that conveys the idea (it will be slower than the synchronous version because the synchronization overhead is larger than the work itself):

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);
}
—━☆沉默づ 2025-01-12 11:46:53

例如,Parallel.For 无法接收引用变量或 Func,因此我们仅限于使用良好的任务。下面是一个示例:

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
...更多相同的。
完毕

Parallel.For can't receive a reference variable or a Func for example, so we are limited to using good ol' tasks. Here's an example:

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

Result:

Executing a task in slot: 0
Executing a task in slot: 1
Executing a task in slot: 2
Executing a task in slot: 3
Waiting for a task slot
Executing a task in slot: 2
Waiting for a task slot
Executing a task in slot: 1
Waiting for a task slot
Executing a task in slot: 3
Waiting for a task slot
Executing a task in slot: 1
Waiting for a task slot
Executing a task in slot: 0
Waiting for a task slot
Executing a task in slot: 3
Waiting for a task slot
Executing a task in slot: 2
... more of the same.
Done

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文