No ratings yet.

 

 

 

 

 

 

 

 

Parallel class in c sharp is very important concept and read there why parallelization is given importance in c sharpwhat is parallel class in c sharp

Code Parallelization With the Parallel Class

Today’s processors usually have multiple cores, and even before that, technologies like hyperthreading gave us the ability to run more than one thread in parallel. Leveraging the multiple available cores can help increase program performance tremendously, and the nice .Net creators gave us a lot of tools to make parallelization easier in .Net 4.0: PLINQ, the Parallel class, the Task class, concurrent collections (ConcurrentStack, ConcurrentQueue, etc), spinning primitives, etc.
In this article i’m going to show you how to use the Parallel class. The class is high-level, easy to use, and can also be used to quickly refactor existing code to leverage multiple cores.

The Parallel class (living in System.Threading.Tasks) provides three important (static) methods:
– Parallel.Invoke: this method takes an array of delegates and runs them in parallel
– Parallel.For: this one works like a for loop, but loop iterations are not executed in sequence but in parallel
– Parallel.ForEach: a parallel foreach loop

Parallel.Invoke

Parallel.Invoke accepts an array of action delegates (you can construct the array yourself, or just pass a number of delegates to Invoke), which it then executes in parallel.
In its easiest form, you use it like this:
static void F1() { Console.WriteLine(“F1()”); }
static void F2() { Console.WriteLine(“F2()”); }
static void F3() { Console.WriteLine(“F3()”); }

static void TestInvoke ()
{
Parallel.Invoke(F1,F2,F3);
}
Calling Parallel.Invoke (F1,F2,F3) will almost always be faster than calling F1(); F2(); F3();. The Parallel class never absolutely guarantees that the calls are parallelized, but there is seldom a reason (especially on modern systems) that it might opt to run them in sequence.
Invoke also handles a large input array well, since it will spawn a number of threads and reuse them, and not create a new thread for each delegate, which would result in very high overhead costs for thread creation and deletion. You can supply an array with thousands of delegates, and Invoke will execute them all, parallelizing as best as it can.
Invoke itself is a synchronous method, it will only return when it has executed all delegates.
When one of the delegates throws an exception, Invoke will still execute all other delegates in its array. All the exceptions thrown by the delegates are bundled in an AggregateException, which Invoke will throw when it finishes.
Invoke also lets you pass an optional ParallelOptions instance as the first argument.
ParallelOptions allows you to do three things:
1. You can set its MaxDegreeOfParallelism property to set the maximum number of threads that Invoke will use
2. You can set its TaskScheduler property, if you have written your own scheduler, which will then be used to decide which delegate to run when and on which thread
3. You can set a CancellationToken
Setting a cancellation token allows you to abort Invoke (remember that when a delegate throws an exception, the exception is swallowed and only rethrown by Invoke after all other delegates have been executed).
To illustrate its usage:
static CancellationTokenSource tok = new CancellationTokenSource();

static void F1() { Console.WriteLine(“F1()”); tok.Cancel(); }
static void F2() { Console.WriteLine(“F2()”); }
static void F3() { Console.WriteLine(“F3()”); }

static void TestInvoke ()
{
ParallelOptions op = new ParallelOptions();
op.CancellationToken = tok.Token;
try
{
Parallel.Invoke(op, F1, F2, F3);
}
catch (OperationCanceledException)
{
Console.WriteLine(“Cancelled!”);
}
}
We create a CancellationTokenSource, and pass its Token property to Invoke through a ParallelOptions instance. Now if the token is set to cancel via CancellationTokenSource.Cancel(), a OperationCancelledException is thrown, but this one is thrown immediately from Invoke, prohibiting the execution of any more delegates in the array (but delegates which are being executed in the moment that Cancel() is called are completed and not interrupted).

Parallel.For

Parallel.For lets you run the code of a for-loop in parallel.
Usually a for loop runs in sequence, one iteration after the other. Parallel.For lets you run the loop iterations on multiple threads in parallel. As with Invoke, the runtime will not start a new thread for each iteration due to the resulting overhead, but will create a number of threads and reuse them, to achieve maximum parallelism with minimum overhead even for a large number of loop iterations.
You use Parallel.For like this:
static void TestFor ()
{
Parallel.For(0, 100, i => Console.WriteLine(i));
}
first you specify the start and maximum number of the loop index. The third argument is an Action<T> (you can either write a method, assign it to an Action<T> delegate, and pass it, or pass a lambda. The start and maximum number, and T, can be int or long.
The above loop example is equivalent to
for (int i = 0; i < 100; i++)
Console.WriteLine(i);
The important difference is that due to the parallel execution of loop iterations, there is no way to predict when which iteration will be executed, so if you run the above example, the numbers from 0 to 99 will be printed in seemingly random fashion to the console.

Parallel.For comes with a handy number of overloads to let you customize its behavior.
The first overload accepts a ParallelOptions object as the third argument. This works like it did for Invoke: you can supply a custom scheduler, set a maximum number of concurrent threads to use, and assign a cancel token.
The last however is not really needed, since another overload provides us an easier way to break out of the loop than using a cancellation token.
Instead of an Action<T>, you can pass an Action<T, ParallelLoopState>, in which you can use the ParallelLoopState to break from the loop.
The first example uses its Stop method:
static void TestFor()
{
Parallel.For(0, 100, (i, state) => {
Console.WriteLine(i); if (i == 50) state.Stop(); });
}
In this case, as soon as the iteration in which i equals 50 is executed, the For loop is stopped. It may only continue iterations which it already has begun to execute. There is no way to predict which iterations will be executed (unless perhaps you write your own scheduler that follows a predictable pattern).
The second example uses the Break method:
static void TestFor()
{
Parallel.For(0, 100, (i, state) => {
Console.WriteLine(i); if (i == 50) state.Break(); });
}
in this case, the For loop will only stop scheduling more iterations when it runs the 50th iteration, and not stop execution immediately. This means that all the loop iterations where i is between 0 and 50 will run. Additional iterations may be run, if they were already started when Break was called.
If you use the overload with the ParallelLoopState, For will return a ParallelLoopResult, which lets you query if the For loop was completed or aborted (with the IsCompleted property) and which was the lowest index that called Break (with the LowestBreakIteration property, which is a nullable int, and will be null if the loop wasn’t aborted).
The ParallelLoopState also has a property ShouldExitCurrentIteration. You can query this property, and break early in an iteration: it will be true if some thread called Break or Stop, Cancel was called on a cancellation token, or an exception occured in another iteration.

There is one more overload, which does not only take one Action parameter, which is executed for each loop iteration, but which takes three. The first Action is run once per thread, when the thread starts. The second is the iteration action, and the third is run once when the thread has finished all iterations. Also, each thread has a local state.
This is a bit more complicated, but you can skip this part, since this overload is rarely needed, because PLINQ solves most of the problems that this overload solves in an easier way.

In the above example, we just used the loop to print the iteration value i. But in many cases you will do some computation with i. For example we can sum all i:
int sum = 0;
for (int i = 0; i < 100; i++)
sum += i;
Console.WriteLine(sum);
This is just a very easy thing, but if you are experienced with multithreaded programming, you will see the problem with this kind of loops: we write to a value (sum) in every loop iteration. If we run the iterations in parallel, we would need to lock around sum when we want to access it:
object mylock = new object();
int sum = 0;
var x = Parallel.For(0, 100, i => { lock (mylock) sum += i; });
Console.WriteLine(sum);
And this is bad, since the lock in every iteration negates any performance improvement we gain from multithreading.
But with the Parallel.For overload we can solve this, and create efficient multithreaded code:
As i mentioned before, each thread will have an associated state, one delegate that will initialize this state once for each thread, and one delegate that is run when the thread has finished its workload.
So what we will do is the following: in the iteration action, we will no longer add i to sum, but we will make our thread-state an int, and add i to this. Since each thread has its own state, we don’t need to lock when writing to it. And when the thread is finished, we add that local sum to the global sum variable. This way, there will be only one lock per thread, and since there usually are just a few threads which each handle many iterations, this will be much faster than locking in every iteration.
The code looks like this:
static object mylock = new object();
static int sum = 0;
static int InitLocalState() { return 0; }
static int RunIteration(int i, ParallelLoopState state, int localsum)
{ localsum += i; return localsum; }
static void FinishThread(int localsum)
{ lock (mylock) sum += localsum; }

static void TestFor()
{
Parallel.For(0, 100, InitLocalState, RunIteration, FinishThread);
Console.WriteLine(sum);
}
InitLocalState sets the thread’s state value to 0. In RunIteration we add i to the state (localsum), and in FinishThread we take the thread’s accumulated value and add it to the global sum.
The above code is pretty lengthy and just there to help understanding. In lambda-form, we can shorten it to:
static void TestFor ()
{
object mylock = new object();
int sum = 0;
Parallel.For(0, 100,
() => 0,
(i, state, localsum) => localsum += i,
(localsum) => { lock (mylock) sum += localsum; });
Console.WriteLine(sum);
}
However, as i mentioned before, you rarely need to write code like this yourself, since PLINQ brings us the same speed as Parallel.For, but with much more user-friendliness, letting us do the above accumulation with
int sum = ParallelEnumerable.Range(0, 100).Sum(i => i);
Console.WriteLine(sum);
And for more complicated problems PLINQ supplies us with the Aggregate() method, so there is not much need for the complicated Parallel.For overload. But knowing that it exists and when and how you can use it can’t hurt 🙂

Parallel.ForEach

Assume you have some simple foreach loop like
foreach (int i in list)
f(i);
with list being a List, and f being some method that takes an int.
You can easily parallelize this loop by rewriting it to
Parallel.ForEach(list, f);
Like Parallel.For, ForEach comes with a lot of overloads, most of them looking like the For overloads:
You can pass a ParallelOptions object, you can use ParallelLoopState and you can use the overload that calls an Init and Finish function once for each thread.
One difference between a for and a foreach loop is the index variable. In a for loop you always have access to it, but in a foreach loop you don’t (but you usually don’t need to). In a normal foreach loop, if you need to know the index of the current element, you can just use some variable counter, and increase it each iteration:
long index = 0;
foreach (int i in list)
{
f(i, index); index++;
}
But using this approach in Parallel.ForEach would be detrimental, since (like with sum in the For example), writing to the index variable each iteration would require locking around it, which would slow it down to the speed of an ordinary single-threaded loop.
Luckily the Parallel class helps us with this problem by supplying an overload that passes the current index to the iteration action:
Parallel.ForEach(list, (i, state, index) => f(i, index));
As you can see, the methods of the Parallel class offer an easy way to parallelize simple loops, and the more complicated overloads offer the flexibility to parallize even complex ones. The possible program efficiency increase you can gain by parallelizing your loops might be well worth the time to rewrite them with the Parallel equivalents

c sharp tips and tricks

what is parallel class in c sharp

Please rate this