Exploring parallel practices

The last example is also a demonstration of the current trend in multithreaded programming. Instead of working directly with threads, we try to put as much of the ugly multithreading plumbing into ready-to-use components. The first level of such abstraction is replacing threads with tasks.

A thread is just an operating system concept; one that allows executing multiple parts of a process simultaneously. When we program with threads, we have to handle all the cumbersome minutiae related to managing operating system threads.

A task, on the other hand, is the part of code that we want to execute in parallel. When we are working with tasks, we don't care how threads are created and destroyed. We just tell the system that we want to run the task and it does the rest. 

The task is a useful step forward, but, for the majority of users tasks are still too low-level. That is why parallel programming libraries that support specialized building blocks (which I like to call patterns) have appeared for all major programming languages.

The first pattern that I looked into was Async/Await, a concept that (to my knowledge) first appeared in C#. It enables you to execute an anonymous method in a background thread. When the background thread finishes its work, the pattern executes a second anonymous method in the main thread. This allows us to clean up after the worker and to update the user interface—something that we must never do from a background thread! This pattern is not a lot different from a task that calls TThread.Queue at the end. The main difference is that, in the latter case, we have to write all the necessary code while Async/Await implements as much of the infrastructure as possible, and you only have to provide the appropriate anonymous methods.

Another interesting pattern is Join. It creates a task, wrapping multiple anonymous procedures. Join executes each procedure in its own thread. More specifically, it should do that, but the implementation from the built-in Parallel Programming Library is broken in the latest Delphi edition, Tokyo. Chapter 7Exploring Parallel Practices, included two different workarounds for the bug.

The next pattern I created from scratch was Join/Await. It extended the Join concept with a simple-to-use termination event that executes in the main thread. This pattern is also capable of catching exceptions in worker code and presenting them to the termination event.

Another simple and interesting pattern is Future. It starts a calculation in a background thread and then enables you to access the result in the main thread. The Parallel Programming Library's implementation does not implement a notification handler that would notify the main thread when computation is finished, but that is easily fixed by using any of the messaging mechanisms I described before.

Also from the Parallel Programming Library comes Parallel for. This very powerful pattern allows you to convert a normal for loop into a parallel version. The pattern starts multiple workers, and processes just a part of the for range in each one. This pattern waits for a parallel loop to finish execution (which blocks the main thread), but that can be easily fixed by wrapping Parallel for in a Future or Async/Await pattern.

There are many other patterns that help you write multithreaded code, but they are not implemented in Delphi's parallel library. Instead of writing them yourself, which may be a very hard task, you should look at existing libraries. My OmniThreadLibrary (www.omnithreadlibrary.com), for example, implements patterns such as Background worker, Timed task, Fork/Join, and more.

One of those patterns is the very useful Pipeline, which can be used to parallelize tasks that can be split into processing stages. This pattern runs each stage in its own thread and connects them with communication queues. As each stage is working on its own copy of the data, there's no need for locking and all the troubles that come with it.

If you have to write multithreaded code, patterns are definitely the way to go. By implementing the most complicated parts of the infrastructure internally, they help you focus on the important part—the problem-specific code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.218.28