|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionAsynchronous processing and background processing was always a must for serious programs serving complex user needs. The Windows NT platform offers a great way to accomplish this, but the implementation was sometimes tedious and always labor intensive. It is the reason why I first studied multithreading options offered by the .NET framework. This article shows three different ways of creating (and using) threads, without communication and synchronization between them. Before you start writing multithreaded programs, bear in mind some guidelines (from MSDN – Threading design guidelines):
Simple threadingLet’s first try the simplest way to create a new thread. The starting point for such a thread is void method with no parameters. Thread creation is done in two steps:
When our public class MainClass{ public void threadMethod(){ ... } public static void Main(){ ... ThreadStart entry = new ThreadStart(threadMethod ) ; Thread thread1 = new Thread( entry ) ; thread1.Start() ; ... } } This sample hides the fact that using C# we do not have a global function, and usually you will start the thread on static class method. public class Tester{ public static void Test(){ ... } } public class MainClass{ public static void Main() ... ThreadStart entry = new ThreadStart( Tester.Test ) ; Thread thread1 = new Thread( entry ) ; thread1.Start() ; ... } } Now we came to the first beautiful part of the .NET framework. We can start threads on class instances and create real living objects. public class Tester{ public void Test(){ ... } } public class MainClass{ public static void Main() ... Tester testObject = new Tester() ; ThreadStart entry = new ThreadStart( testObject.Test ) ; Thread thread1 = new Thread( entry ) ; thread1.Start() ; ... } } Timer threadsA common use of threads is for all kinds of periodical updates. Under Win32 we have two different ways: window timers and time limited waiting for events. .NET offers three different ways:
For inexact timing we use the window timer. Events raised from the window timer go through the message pump (together with all mouse events and UI update messages) so they are never exact. The simplest way for creating a The
If we put this in code we get: using System; using System.Threading; // class for storing current state public class StateObj{ ... } // class that will work on timer request public class TimerClass{ public void TimerKick( object state ){ StateObj param = (StateObj)state ; // do some work with param } } // usage block { ... // prepare state object StateObj state = new StateObj() ; // prepare testing object – not necessary when cb is static TimerClass testObj = new TimerClass() ; // prepare callback delegate – careful on static methods TimerCallback tcb = new TimerCallback( obj.TimerKick ) ; // make timer long waitTime = 2000 ; // wait before first tick in ms long periodTime = 500 ; // timer period Timer kicker = new Timer( tcb, state, waitTime, periodTime ) ; // do some work ... kicker.Change( waitTime, periodTime ) ; // do some more work ... kicker.Dispose() ; ... } For waitTime and periodTime you can use The final timing options come from the
using System.Timers ; void TickHandler( object sender, EventArgs e ){ // do some work } // usage block { ... // create timer Timer kicker = new Timer() ; kicker.Interval = 1000 ; kicker.AutoReset = false ; // add handler kicker.Tick += new EventHandler( TickHandler ) ; // start timer kicker.Start() ; // change interval kicker.Interval = 2000 ; // stop timer kicker.Stop() ; // you can start and stop timer againg kicker.Start() ; kicker.Stop() ; ... } I should mention a few things about using the
Thread poolingThe idea for making a pool of threads on the .NET framework level comes from the fact that most threads in multithreaded programs spend most of the time waiting for something to happen. It means that thread entry functions contain endless loops which calls real working functions. By using the There are two important facts relating to
The most useful use of a
In C#: // status information object public class StatusObject{ // some information } // thread entry function public void someFunc( object obj, bool signaled ){ // do some clever work } // usage block { ... // create needed objects AutoResetEvent myEvent = new AutoResetEvent( false ) ; WaitOrTimerCallback myThreadMethod = new WairOrTimerCallback( someFunc ) ; StatusObject statusObject = new StatusObject() ; // decide how thread will perform int timeout = 10000 ; // timeout in ms bool repetable = true ; // timer will be reset after event fired or timeout // add to thread pool ThreadPool.RegisterWaitForSingleObject( myEvent, myThreadMethod, statusObject, timeout, repetable ) ; ... // raise event and start thread myEvent.Set() ; ... } A less common use of a thread pool will be (or at least should be, be aware of misuse) adding threads to be executed when the processor is free. You could think of this kind of usage as "OK, I have this to do, so do it whenever you have time". Very democratic way of handling background processing which can stop your program quickly. Remember that inside the thread pool you have only one thread working (per processor). Using thread pool this way is even simpler:
// status information object public class StatusObject{ // some information } // thread entry function public void someFunc( object obj, bool signaled ){ // do some clever work } // usage block { ... // create needed objects WaitCallback myThreadMethod = new WairOrTimerCallback( someFunc ) ; StatusObject statusObject = new StatusObject() ; // add to thread pool ThreadPool.QueueUserWorkItem( myThreadMethod, statusObject ) ; ... } Some notes on thread pool:
ConclusionThis article shows the way I used to find out secrets of .NET multithreading. When you try using this, be careful on thread synchronization. This is also subject of my next article, where I will show all different ways of synchronization that .NET offers. For more information read articles in MSDN library:
|
||||||||||||||||||||||