Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How often does Parallel.For invoke localInit?

I've been experimenting with Parallel.For. In particular, overloads that support thread-local data such as

public static System.Threading.Tasks.ParallelLoopResult For (long fromInclusive, long toExclusive, System.Threading.Tasks.ParallelOptions parallelOptions, Func localInit, Func body, Action localFinally);

According to the documentation

The localInit delegate is invoked once for each thread that participates in the loop's execution

However I think my example below contradicts it

var threads = new ConcurrentBag<int>();
ValueTuple LocalInit()
{
    threads.Add(Thread.CurrentThread.ManagedThreadId);
    return new System.ValueTuple();
}
ValueTuple Body(long i, ParallelLoopState _, ValueTuple state) 
{
    Thread.Sleep(100);
    return state;
}
void LocalFinally(ValueTuple state) { };

Parallel.For(0L, 1000L, new ParallelOptions(), LocalInit, Body, LocalFinally);

Console.WriteLine($"{threads.Count} inits between {threads.Distinct().Count()} threads");

It prints a message such as

79 inits between 13 threads

What's going on?

like image 240
Colonel Panic Avatar asked Oct 16 '22 14:10

Colonel Panic


1 Answers

Try recording task id Task.CurrentId instead of thread id.

var threads = new ConcurrentBag<int>();
var tasks = new ConcurrentBag<int>();
ValueTuple LocalInit()
{
    threads.Add(Thread.CurrentThread.ManagedThreadId);
    tasks.Add(Task.CurrentId ?? throw new Exception());
    return new System.ValueTuple();
}
ValueTuple Body(long i, ParallelLoopState _, ValueTuple state) 
{
    Thread.Sleep(100);
    return state;
}
void LocalFinally(ValueTuple state) { };

Parallel.For(0L, 1000L, new ParallelOptions(), LocalInit, Body, LocalFinally);

Console.WriteLine($"{threads.Count} inits between {threads.Distinct().Count()} threads");
Console.WriteLine($"{tasks.Count} inits between {tasks.Distinct().Count()} tasks");

This prints

87 inits between 16 threads
87 inits between 87 tasks

The docs are wrong. They should instead say

The localInit delegate is invoked once for each task that participates in the loop's execution

There can be more tasks than threads. Always number of threadsnumber of tasksnumber of iterations.

like image 85
Colonel Panic Avatar answered Oct 20 '22 05:10

Colonel Panic