Chapter 7 - Tasking for concurrent programming.
Many more programmers are having to work in multithreaded environments today, most modern operating systems support threads (or light-weight processes) to some extent. The problem with threads is that they are not standard and so are not portable. Every operating system has a differing set of API's and frequently differing scheduling and priority rules. Thread programming is also notoriously difficult to manage in C and difficult to encapsulate in C++. Neither C or C++ provide any support for important issues in multi-threaded programming, features like reentrant functions, inter-process communication (IPC).
Unlike C++ Ada defines a model for concurrent programming as part of the language itself. Few other languages (Occam and Java[19] are examples) provide language level concurrency, other languages (Modula-3) provide a concurrency model through the use of standard library abstractions. In Ada there are two base components, the task which encapsulates a concurrent process and the protected type which is a data structure which provides guarded access to its data. This chapter will deal with how to use these features to develop responsive, high performance applications utilising one of the few truly cross-platform models for concurrent processing.
7.1 Tasks and Threads.
The task is an active component, it is the Ada construct encapsulating a light-weight process, it provides a simple model for executing multiple code blocks concurrently.
For those who have not worked in a multi-threaded environment you might like to consider the advantages. In a non-multi-threaded UNIX (for example) the granularity of concurrency is the process. This process is an atomic entity to communicate with other processes you must use sockets, IPC etc. The only way to start a co-operating process is to initialise some global data and use the fork function to start a process which is a copy of the current process and so inherits these global variables. The problem with this model is that the global variables are now replicated in both processes, a change to one is not reflected in the other. In a multi-threaded environment multiple concurrent processes are allowed within the same address space, that is they can share global data.
As an example consider the word processor I am using to write this book, it is version 7.0 of Microsoft Word and it has some great features. One which has been very useful is that it spell checks words as I write them underlining them in red if they are incorrect (some reviewers have pointed out that it did not do a very good job!). It also paginates the text as I write which with the amount of formatting involved must be a relatively complex task. Traditionally there one of two approaches had to be taken to such intensive tasks, first it had to be an off-line process initiated from a menu or periodically the word processor would stop, refuse further input and repaginate.
The reason for the use of such user-unfriendly mechanisms is that such applications had a single thread of execution which has to handle the user-input and the graphical output and cannot spare time for more complex tasks. When I run the Microsoft process viewer application I notice that Word now has three threads running, and although I do not know what each thread is doing it is plain to see that it is the introduction of these threads that allows the word processor to offer me so many new features.
The breaking of these application performance bottlenecks is the most common application for threads although they are often used within server software to manage connections from clients in a more secure and controlled manner than was previously possible with multiple process environments.
Bearing this application example in mind we will start by looking at how to program threads in the Windows-NT environment.
Copyright ©
1996 Simon Johnston &
Addison Wesley Longman