WHAT'S NEW?
Loading...
Showing posts with label Threads. Show all posts
Showing posts with label Threads. Show all posts

Index


  • Introduction
  • Using the Parallel class
  • Using async and await
  • Using the Parallel Language Integrated Query (PLINQ)
  • Using concurrent collections

Introduction

This post is part of a series of post to help you prepare the MCSD certification, particularly the certification exam 70-483, based on the book:


You will find all the code available in the following GitHub repository. Lets talk a little bit about threads and how they work using a .Net development environment.

Using the Parallel class

System.Threading.Tasks contains also another class we have not checked yet in previous posts. The Parallel class has a couple of static methods (For, ForEach and Invoke). This doesn't mean you should replace all your loops with parallel loops.

Use the parallel class only when your code doesn't have to be executed sequentially. When you have a lot of work to be done that can be executed in parallel. For smaller work sets or for work that has to synchronize access to resources, using the Parallel class can hurt performance.

In the following example you can see how to use the method For and ForEach when you want to run parallel operations without sharing resources. The third block of code is using the ParallelLoopResult to show you how to break a running operation when the loop completes the first half of iterations. At that point, the IsCompleted value equals to false and the LowestBreakIteration is 500.


Using async and await

When accessing I/O components on the primary thread, Windows typically, pauses your thread in order to release the CPU. Asynchronous solves this limitation but it has some complexity when writing code. For this reason, Microsoft introduced async and await operators when C#5 was released improving responsiveness and scalability.

A method marked with async starts on the current thread and it contains calls marked with await, which are our parallel tasks. Using the await keyword, the compiler can be aware if an operation has finished (continuing synchronously) or not (hooking up a continuation method that should run when the Task completes.

In the following example you get the taste of how this great tool works. Note: you probably need to import the System.Net.Http dll into your project if your IDE can't resolve the HttpClient class dependency



See how a separated method is decorated with the "async" keyword because the entry point for an app can't be marked as async.

Keep in mind, you are not making your app performing any better just by using tasks and awaiting them. You're improving responsiveness.

Normally, when an exception happens you expect an AggregateException to be triggered.

Another relevant point is the SynchronizationContext which connects its application model to its threading model. It abstracts how different applications work and helps you end up on the right thread working with a WPF app or ASP... This means, for example in an ASP app, tasks will find the right user related information (current user, culture,...) when get finished.

In some cases you don't need this data and could run an asynchronous task without the context. Calling the method ConfigureAwait(false) you disable the context saving process and you get better performance. See how in the following example we use a couple of awaits without saving the context. We are getting some text from internet and placing those lines in a text file.



With async/await you should keep in mind:

  1. Never return void within an async method. Return void is only valid for asynchronous events.
  2. Never marked a method as async without any await call decorated.

Using the Parallel Language Integrated Query (PLINQ)

As you are probably aware, LINQ (Language –Integrated Query) is a popular syntax used to perform queries over all kind of data. PLINQ is the parallel version and lets you run extended methods like for example: Where, Selet, SelectMany, GroupBy, Join, OrderBy, Skip and Take.

It's the runtime who decided weather to run in parallel or not. You can force to run in parallel by calling: WithExecutionMode() method. Behind the scenes the CLR will generate different Tasks objects to perform the asynchronous process.

By default, PLINQ uses all the available processor in the CPU (up to 64) but you can always limit this by calling: WithDegreeOfParallelism() method, and passing an integer which represents the number of processors to be used. Note, this parallelism does not guarantee any particular order, see following example:


There is also a way to get this results ordered by calling AsOrdered() function, which still perform a parallel action but buffering the results from the different tasks.

In some cases you might want to perform a sequential operation after a parallel one. By calling AsSequential(), PLINQ will return the answer as non parallel. In the following example we are using Take() and it might mess up the result unless we call AsSequential() first:


On the other hand, if you don't mind get not ordered results you might want to perform a ForAll() function call: parallelResult.ForAll(e => Console.WriteLine(e)); It does not need all the results before starting, but, it will remove any sort order specified. In some cases, you can end up with exceptions in your queries which will be handled using AggregateException. This will show a list of all exceptions triggered during the parallel execution.

Using concurrent collections

Asynchronous access to data always need to be concurrent safe, it means, more than one thread can modify the same data at the same time. There are some collections in .Net developers use to ensure concurrency is safe used like:

  • BlockingCollection<T>
Thread-safe entity handles access to its information. In reality it's a wrapper around a collection, by default, the ConcurrentQueue. For this example, notice how there is one task which listens for new items being added to the collection blocking if no items are available, while other task adds items to it.


You could call the CompleteAdding() method to signal that no more items will be added so other threads waiting for items won't be blocked any more. We can improve the algorithm by using GetConsumingEnumerable() method which returns an Enumerable object that blocks until it finds a new item. With this you can use a foreach with your BlockingCollection to enumerate it.

  • ConcurrentBag<T>
It is, effectively, just a bag which allow you to perform Add, TryTake and TryPeek with duplicates an no particular order. Sometimes I found TryPeek not very useful because you can end up with another tread deleting the item before you actually access it. ConcurrentBag implements IEnumerable<T> so you can iterate over an snapshot of the collection. If new items are added after you start iterate they won't be visible until you start a new iteration.
  • ConcurrentDictionary<TKey, T>
It stores key and value in a thread-safe manner, you can add, remove and update (if exist) items as atomic operations (a single operation will be performed without other threads interfering). TryUpdate() will check first if the current value is different from the new one. AddOrUpdate() will push a new item to the Dictionary if it doesn't exists. GetOrAdd() gets the current value of an item if it's available, if not, it will add the new value.
  • ConcurrentQueue<T>
  • ConcurrentStack<T>
Stack is a LIFO collection and a Queue is a FIFO collection. ConcurrentStack implements Push() (save item) and TryPop() (get item) methods, unfortunately you can never be sure whether are items within the queue due to several threads running in parallel. You can also add or remove bulk items with: PushRange() and TryPopRange().

ConcurrentQueue provides Enqueue (push) and TryDequeue (get) to add and remove items from the queue. It also contains a TryPeek() and it implements the IEnumerable class by making a snapshot of the data.


References

https://www.microsoftpressstore.com/store/exam-ref-70-483-programming-in-c-sharp-9780735676824
https://github.com/tribet84/MCSD/tree/master/mcsd

Index

  • Introduction
  • Multithreading and asynchronous processing
  • Thread pools
  • Using Tasks
  • Canceling Tasks

Introduction

This post is part of a series of post to help you prepare the MCSD certification, particularly the certification exam 70-483, based on the book:


You will find all the code available in the following GitHub repository. Lets talk a little bit about threads and how they work using a .Net development environment.

Multithreading and asynchronous processing

A process is used to isolate applications and threads run in the context of that process. This way, it makes easier for the OS to manage different apps, managing crashes and context switching (slots of time provided to each app to be executed by the CPU). The idea behind is simply run threads during a predefined slot of time, when the time permitted for an app to run is over, the CPU switches to other thread to be executed.

Use the Threading class for small asynchronous tasks in your applications.

See how in the following example you can create a new thread using the System.Threading namespace. Notice how t.Join() is called to wait for the new thread to finish.


Thread.Sleep(0) is a way to force windows to switch context instead of waiting for the whole slot of time to finish.

Both elements, the process and the thread have a priority. Low priority could be useful for a screen saver. Such application shouldn't compete for CPU time. A higher priority should be used only in certain scenarios because by default it's normal priority.

Foreground and background threads are important to keep an app alive. When all foregrounds threads are finished then background threads can be terminated without waiting for them. By default a thread will be foreground type but you can change this by: myThread.IsBackground = true;

ParameterizedThreadStart is a delegate you can pass in the instantiation of your threads in order to pass parameters into your thread. In this example see how to pass an integer as a parameter in your thread:


Thread.Abort() method is used to stop a particular thread. Not a good practice because is executed by another thread and it throws a ThreadAbortException in the target, which can potentially leave a corrupt state. A better way to do this is by using a shared variable as you can see in the following example:

In the previous example we start a never ending task which will show in the screen the word "Running..." every second. The main thread of our app will stop in the ReadKey() method waiting for a key to be pressed by the user. As soon as the user press any key the "stopped" variable will turn into true, which will finish the while loop so the thread will be ended and the thread will be merged into the main app thread in th Join() call.

A thread has its own call stack that stores all the methods that are executed. Local variables are stored in the call stack and are private to the thread. But you can create global variables by decorating with the "[ThreadStatic]" attribute. These variables will act locally within the threads, it doesn't matter if you increase the value of a [ThreadStatic] integer variable in several threads because the variable is not shared between threads.

If you want to use local data and initialize it for each thread you can use the ThreadLocal<T> class. This class takes a delegate to a method that initializes the value;


See how in the previous example we use the Thread.CurrentThread class which contains the thread execution context (CultureInfo to represent dates, currency, sorting order for text, etc. for a certain culture), principal (security), priority and more. When a thread is created, the runtime ensures that the initiating thread's execution context is flowed to the new thread, so the new thread has the same privileges as the parent thread.

This copy of data has a process time cost which we can avoid if not needed by using ExecutionContext.SupressFlow method.

Thread Pools

Working directly with the Thread class, you create a new thread each time and the thread dies when you're finished with it. This creation of a new thread is an operation which has time / resources costs associated.

A thread pool reuses threads. Instead of letting a thread die, you send it back to the pool where it can be used when a new request comes. The thread pool limits the available number of threads, which means, you could get lesser parallelism than using Thread class.

On the other side, imagine a web server managing too many user requests. At some point it could become unresponsive using Threads and start queuing up the work load. You can save this easily by using Thread pools because it automatically manages the amount of threads it needs and if threads are not used for a long time, it automatically removes them.

Using Tasks

Queuing a work item into a thread pool can be useful but there is no built-in way to know when the operation is finished and what the return value is. Here is when we introduce Tasks. They represent some work that has to be done. Tasks can tell you if the work is completed and what is the returning value. By default, the task scheduler uses threads from the pool.

*** Use Taks when you want to keep you interface thread free (responsiveness) or you want to parallelize your work on multiple processors.

See an small example of how to use it.


Next to Task is the Task<T> class, which you can use to return a value from a task. In this example we create a Tasks<int>. Attempting to read the Result property will wait until the Task is finished before continuing.


You could save this inconvenience by adding a continuation task. This means that you want to run another operation as soon as the task finishes.


The ContinueWith method has a couple of overloads to configure when the continuation will run. Continuation methods will run when an exception happens, the Task is canceled or the Taks completes.


A Task can also have several child Tasks. The parent Task finishes when all the child tasks are ready. See an example below:


As you can see, finalTask runs after the parent Task is finished, and the parent Task finishes when all three children are finished. You had to create three Tasks all with the same options. To make the process easier you can use a TaskFactory with a certain configuration to create your tasks.


Next to Wait on a single Task, you can also use the method WaitAll to wait for multiple Tasks to finish before continuing execution. As an example: Task.WaitAll(tasks); being tasks an array of several Task objects.

Next to WaitAll, you can use WhenAll to schedule a continuation method after all Tasks have finished. This WhenAll function is used in the same way as we did before with the method ContinueWith.

Finally, instead of waiting for all of them to be finished you might want to wait until one is finished, for that reason you could use WaitAny method, see example below:


The while loop will be finished after three seconds when all the tasks are completed.

Cancelling tasks

When working with long running threaded operations, in some cases, you might want to stop the process, for this reason .Net offers you a class to signal a cancel operation within a task: CancellationToken. You just pass a CancellationToken into a class which will be periodically monitored to see if the cancellation is requested.

The operation will just end when cancellation is requested. Outside users won't see anything different because the task will have a RanToCompletion state but if you want to signal those users you can trigger a OperationCanceledException by using the ThrowIfCancellationRequested() method. See here:


That's it for today, I encourage you to continue learning more about Threads in my next MCSD post. See ya!

References

https://github.com/tribet84/MCSD/tree/master/mcsd