简体   繁体   中英

How Can I Use Parallel For in TPL instead of While

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

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

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM