So, a thread can be considered a user level unit of parallel execution, implemented by a lightweight process or set of lightweight processes.. They allow: - to avoid blocking the GUI by
Trang 1
Microsoft.NET Architecture and the C# Language
A semester course for 4th year students
Comments to the presentations
Prof Vladimir O Safonov,
St Petersburg University
Email: v_o_safonov@mail.ru
WWW: http://user.rol.ru/~vsafonov
Lecture 13.
Slide 2.
Multithreading and synchronization in NET is the main subject of this lecture.
Also, debugging and tracing features will be considered
Slide 3.
Thread is a parallel branch of execution, or control flow, of an application
that should perform a logically independent part of the application's task
The history of parallel processing starts with E Dijkstra's paper on synchronization
published in 1966
One of the first operating systems that supports parallel processing was UNIX
whose first version was shipped in 1970
However, in UNIX a process is heavyweight in the sense that each process has its separate
virtual address space, which is too resource comsuming
Lightweight process is a new kind of process first appeared in Windows NT and Solaris.
It shares the same virtual address space with its parent process.To create a lightweight process,
it is enough just to create a new stack for its execution
So, a thread can be considered a user level unit of parallel execution, implemented by
a lightweight process or set of lightweight processes
Threading libraries in different operating systems have different APIs
So, an important task was to provide a hardware platform independent layer of threading for the users
Before Java and NET neither of programming languages or platforms (e.g C or C++)
provided a unified threading model "at the application layer"
Slide 4.
Trang 2Threads are a basis for solving tasks "in parallel style".
The main advantages of using threads are as follows
They allow:
- to avoid blocking the GUI by delegating to a separate thread processing GUI events;
- to manage priorities of subtasks whose solutions were delegated to separate threads;
- to organize potentially long-term tasks, like communicating to network or a DBMS server The shortcoming of threads is related to inevitable overhead to context switching
from one thread to another
Another one is making the programming model, design and implementation more
complicated, taking into account the parallelism, as compared to traditional
"sequential programming style"
Slide 5.
On NET platform, threading support APIs are located in the System.Threading namespace.
To decrease the complexity of design multi-threaded applications, NET provides a set of patterns for design and asynchronous programming
They help to hide the fact of using threads and support delegates for callback
methods used in asynchronous schemes
Slide 6.
The architecture of a NET process is shown on the scheme Actually, a process is
a running application A process can contain one or more threads
There can be shared data used by several threads, and in addition each thread can have its thread-specific data
Slide 7.
Another very important, absolutely new concept of NET is application domain
already considered before
Application domains are mini-processes in CLR
They enable application isolation for protecting against faults, designating the borders
of type visibility and making security checks
Slide 8.
The CLR treats executable applications as application domains
The role of application domains in CLR is similar to the role of processes in
an operating system
One process can contain many application domains but each application domain
can only belong to one process
Managing domains is much cheaper than managing processes
Trang 3Switching an operating system thread between application domains
is much faster than between processes
Slide 9.
Properties of application domain are similar to environment variables.
They can be read and updated by pairs of GetData and SetData methods
whose first argument is the name of the property
The set of application domain properties is extensible
They are accessible either from inside or from outside the domain
Domain properties are used when searching assemblies
The GetAssemblies method returns an array of references to all assemblies loaded
into an application domain
The current application domain is determined by the CurrentDomain
static property of the AppDomain class.
Slide 10.
An application domain is a scope of types, static variables and objects.
All of them belong to some definite application domain
and are unloaded together with their host domain
Slide 11.
Application domains protect users against faults inside the operating system's process
Each AppDomain has its own filter of unhandled exceptions, provided by the
UnhandledExceptionsEvent
and the related delegate
Each AppDomain can also define its own security policy level to enable a "top-of-stack" set of
permissions
for the methods to be executed within the application domain
This can be done via the SetAppDomainPolicy method.
In addition, application domains are independently compiled and debugged
The borders of an application domain are closed for objects, except for
passing them via the remoting mechanism.
Slide 12.
Here are the basic methods of domain programming API, provided by the AppDomain class:
CreateDomain - create a new application domain;
Unload - unload an application domain and release all its resources;
Trang 4Load - load a given assembly into a given domain;
ExecuteAssembly - execute in a given domain the assembly contained in a specified file;
CreateInstance - create a new instance of a specified type in a given assembly;
DoCallback - call a given delegate in a given assembly.
Slide 13.
This example demonstrates how to use the CreateDomain and ExecuteAssembly methods
to make child domains and execute assemblies
In the call of CreateDomain, the trailing null arguments stand for the security policy and
the for domain initialization object
They can be omitted and one argument only, the name of the new domain, can be passed
In the call of ExecuteAssembly, the trailing null arguments stand for the supplied evidence for the
assembly
and for the array of arguments to the assembly entry point
They can be omitted and one argument only, the name of the assembly file, can be passed
Slide 14.
To pass objects or their references across application domain borders, marshaling is required.
There are two kinds of marshaling - marshal by reference and marshal by value.
The former means that the object remains within the limits of its host domain, its
state is not copied, and a proxy object on the client is created to get access to the object
The latter means that the state of the object is copied and actually the contents of the object
is passed to the client in some standard serialized form
Anyway, the class of the object should be marked as Serialized, otherwise the object
cannot be marshaled at all
The RemotingServices.Marshal method is used to create an ObjRef - a serializable
representation of the object It should be parsed to create a proxy for the object.
To marshal an object by reference, the type of the object should be a descendant of
MarshalByRefObject.
Otherwise the object is marshaled by value
Slide 15.
Now let's start with threading in NET.
As in Java, a new thread is initiated by creating a Thread object,
and started to execute by the Start method,
as shown on the example
But, unlike Java, to actually start execution of a thread,
the method to be executed on the thread is defined by a special delegate
Trang 5whose type is indicated as ThreadStart.
Slide 16.
A thread has a name, priority and a flag to indicate whether it is a foreground thread
or a background thread
All the three items are properties of the thread.
Name is used to refer to the thread,
priority is used to manage the order of executing threads.
A thread is background if it does not prevent the host process from terminating.
In Java, a similar concept is daemon thread.
Slide 17.
Managing threads in NET is made very similar to Java
The static Sleep method blocks a thread for a given number of milliseconds.
The Suspend and Resume methods serve to delay executing of the thread, and resume it again.
It should be specially noted that, no matter these methods are presented to the users,
they are potentially very dangerous because their inappopriate use can lead to deadlocks For example, if a thread is suspended within its critical section, after locking some common resource but before unlocking it, this may cause a deadlock because other threads
can permanently wait for this resource to get unlocked
For this reason, meanwhile, the authors of Java had to claim these methods to be deprecated.
The Join method should be used to safely wait for another thread to terminate.
So, the pair of Start and Join methods can be regarded as an analogy to
the classical pair of fork and join methods in UNIX.
The WaitHandle.WaitAny and WaitHandle.WaitAll methods are used for some events to occur.
The events are related to availability of common shared resources
WaitAny waits for any of the specified WaitHandle array elements to receive a signal that
the event has occurred (and return the index of the corresponding array element),
WaitAll waits for all the elements to receive a signal.
These methods support event synchronization paradigm for NET threads
Slide 18.
Thread static storage (or data) mechanism is necessary to support
thread-specific information to be retrieved and updated
The ThreadStatic attribute marks a field to be unique for each thread.
Addressing the field is made as usual, taking into account it is quite safe,
since the field is unique for each thread
Trang 6Similar features are supported in various operating systems like latest Windows
or Solaris, and also in Java
Slide 19.
Interrupting and terminating threads is done similar to Java,
by generating special kinds of exceptions within the thread to be
interrupted or terminated
The Interrupt method awakens a given thread and generates the
ThreadInterruptedException in it.
The awakened thread should catch and process this exception
Such situation can occur, for example, after calling Sleep.
So, as well as in Java, Interrupt means to interrupt the state of sleeping,
rather than to interrupt the thread itself.
The Abort method attempts to abort a given thread.
But, instead of actual abortion, a ThreadAbortException is generated
in the thread to be aborted
The following depends on the way the thread processes the exception
This specific kind of exception is re-thrown at the end of each try-catch block,
unless the thread calls the ResetAbort method that clears the state of automatic rethrowing.
All in all, such way of thread abortion does not fully guarantee the termination
of the thread
To guarantee that, the Join method should be invoked on that thread.
Slide 20.
The classical problems of race condition and threads synchronization arise
for NET threads as well as for any other kinds of parallel processes
The origins of these problems are very well described in the classical and pioneering paper by Edsger W Dijkstra "Cooperating sequential processes" in 1966
Race condition is non-controlled order of access to shared resources.
Deadlock is a chain of suspended threads such that each of them but the last
is waiting for the next thread in the chain, and the last one is waiting for the first one
.NET offers a number strategies how to avoid race condition and deadlocks:
- having a thread local copy of the sata;
- synchronized context;
- synchronized block of code;
- manual synchronization
All of them will be considered in the next lecture