Creating Multi-Threaded Applications with the .NET 2.0 Framework Sub Topics: Safe Synchronization for Multi-threaded Data Access and Thread Safe Callbacks via Delegates |
Threads with .NET are fairly easy to create and can be used for all sorts of
tasks. A good example are background threads which send
and receive data to other machines on a network. Server programs utilizing
TCP/IP typically spawn a dedicated listener thread to sit and
wait for incoming connections. Once the connection is completed the server
thread will either pass the connection off to an existing worker
thread or it may spawn a new thread dedicated to service the incoming
client. The following code snippets are from a compact framework
to full framework application I wrote to collect data on Mobile devices and
push it up to a windows server application. All of the network
IO occurs in background threads. This keeps the GUI free and open on both the
client and server ends to handle user interaction. The
first snippet is an example of creating TCP server and worker threads
using the Thread class and the ThreadStart delegate.
class GenericTcpServer { // The above is instantiated by a call from the Forms constructor public partial class Form1 : Form { |
To quote from the class library reference "When a thread is created, the new
instance of the Thread class is created using a constructor
that takes the ThreadStart delegate as its only parameter. However, the
thread does not begin executing until the Start method is
invoked. When Start is called, execution begins at the first line of the
method referenced by the ThreadStart delegate." I never realized
that ThreadStart was a delegate. Special note for the VB folks again from
the library reference, "Visual Basic users can omit the
ThreadStart constructor when creating a thread. Use the AddressOf
operator when passing your method to the Thread constructor".
The above code simply waits on a configured port number for an incoming
connection. Once connected a worker thread is spawned which
consumes the data and places it into an ArrayList. This is
then passed onto a transaction queue (another piece of code not shown
here)
Note: there is also a Thread.Sleep() method as well which will place the thread
in an idle state for the specified number of milliseconds.
Along with threading go other issues such as synchronization and data access
safety. In creating a windows server application I was
shocked to see that the synchronization attribute did not work all of the
time. After talking with a few people this was confirmed. Their
suggestions were to either use the SyncRoot method along
with the lock keyword or utilize a try catch model
to handle the case where
the exception occurs. Actually I had done the latter in order to handle
the error but even with that I had a creepy feeling about using the
synchronization attribute. Here is an example of a classic consumer-producer
process which is reading and writing transactions. Different
threads call into this function. Note: the first example will fail
occasionally, the second will not!
// * bad, bad, bad this will fail every once in a
while |
Note: In the above example SyncRoot comes with the ArrayList,
but you can allocate and use a SyncRoot as a
System.Object
class. The lock keyword keeps the function locked between the code block
enclosed by the braces.
Additionally you may want to have a thread call back into another thread to
update data. For example a complex calculation has been
completed by a background thread and you want to display the result on the
Form. You can use delegates to do this but you will want to
marshal the data you are passing as well as ensure thread safety by using
the Invoke method. Otherwise you will get an exception!
Here is an example of a main thread creating a delegate and passing this as an
instance member to a thread which it spawns. The thread
calls back the main thread via a delegate with the Invoke
method in order to update its GUI with data.
namespace CollectorServerMobile {
class GenericServerTCPThreadObject {
// the caller thread, when ready to notify the main thread |
Note: this is similar to the code in the first example, although this time we
are creating a delegate as a member field in the class. When we
spin the thread we pass "this" to it which then gives the thread the ability to
make a callback to the delegate function. Remember that
delegates are just type safe function pointers. Unfortunately just calling the
delegate from the thread creates chaos and if you do it your
program will perform the callback but will create an unhandled exception when
it attempts to access any data in the called function. To
keep it clean use the Invoke function.
You can take this example further by creating multiple delegate functions to
perform different operations as required by your
application.
Creating
Multi-Threaded Applications with the 2,0 Framework
Device
Programming
Feedback/Contact paulzazzarino@3zwireless.com .
Copyright 2006 3zwireless Ltd, This page last updated on 03/2006