Sunday, May 30, 2010

programs on processes and threads

Processes and Threads


9.1 Processes

A process in its simplest form is a running application. The Process class provides

access to local and remote processes and enables us to start and stop local system

processes.

The Process.Start() method starts a process resource by specifying the name of

a document or application file and associates the resource with a new process component.

Thus we can start other applications from within our application.

The program below launches notepad.exe, winhlp32.exe and asynchronously counts

up from 0 to 100001, then destroys the notepad.exe process using the Kill()

method. It counts again from 0 to 2000 and then kills the winhlp32.exe process.

Each process has an Id number.

// process1.cs

using System;

using System.Diagnostics;

class Processtest

{

public static void Main()

{

Process p = Process.Start("notepad.exe");

string name = p.ProcessName;

int id = p.Id;

DateTime started = p.StartTime;

Console.WriteLine("name = " + name);

Console.WriteLine("id = " + id);

Console.WriteLine("StartTime = " + started);

114

9.2. THREADS 115

Process w = Process.Start("winhlp32.exe");

for(int i=0;i<100001;i++) Console.WriteLine(i);

p.Kill();

for(int j=0;j<2000;j++) Console.WriteLine(j);

w.Kill();

}

}

If we also want to load a file (for example process2.cs) with notepad.exe we

change this line to

Process p = Process.Start("notepad.exe","c:\\csharp\\process2.cs");

The detect process completion we use the method

WaitForExit()

9.2 Threads

9.2.1 Introduction

A thread is an execution stream within a process. A thread is also called a lightweight

process. It has it own execution stack, local variables, and program counter. There

may be more than one thread in a process. Threads are part of the same process

which execute concurrently. In .NET Base Class Library (BCL) the System.Threading

namespace provides various classes for executing and controlling threads.

The following program Threads0.cs is the simple example. We create a Thread

instance by passing it an object of ThreadStart delegate which contains a reference

to our ThreadJob method. Thus the code creates a new thread which runs the

ThreadJob method, and starts it. That thread counts from 0 to 1 while the main

thread counts from 0 to 5.

// Threads0.cs

using System;

using System.Threading;

public class Threads0

{

static void Main()

{

ThreadStart job = new ThreadStart(ThreadJob);

Thread thread = new Thread(job);

116 CHAPTER 9. PROCESSES AND THREADS

thread.Start();

int i = 0;

while(i < 6)

{

Console.WriteLine("Main thread: {0}",i);

i++;

} // end while

} // end Main

static void ThreadJob()

{

int i = 0;

while(i < 2)

{

Console.WriteLine("Other thread: {0}",i);

i++;

} // end while

} // end ThreadJob

}

An output could be

Main thread: 0

Main thread: 1

Main thread: 2

Main thread: 3

Main thread: 4

Main thread: 5

Other thread: 0

Other thread: 1

At another run the output could be

Other thread: 0

Other thread: 1

Main thread: 0

Main thread: 1

Main thread: 2

Main thread: 3

Main thread: 4

Main thread: 5

9.2.2 Background Thread

In the program threadtest2.cs the Thread t is set to the background thread

9.2. THREADS 117

t.IsBackgound = true;

The thread will terminate when the application terminates.

// threadtest2.cs

using System;

using System.Threading; // for Thread class

class ThreadTest2

{

public static void SayHello()

{

for(int i=1;;i++)

{

Console.WriteLine("Hello {0}",i);

}

}

public static void Main()

{

Thread t = new Thread(new ThreadStart(SayHello));

t.IsBackground = true;

t.Start();

for(int i=1;i<1001;i++)

{

Console.WriteLine("Bye {0}",i);

}

}

}

9.2.3 Sleep Method

Once a thread has been started it is often useful for that thread to pause for a

fixed period of time. Calling Thread.Sleep causes the thread to immediately

block for a fixed number of milliseconds. The Sleep method takes as a parameter

a timeout, which is the number of milliseconds that the thread should remain

blocked. The Sleep method is called when a thread wants to put itself to

sleep. One thread cannot call Sleep on another thread. Calling Thread.Sleep(0)

causes a thread to yield the remainder of its timeslice to another thread. Calling

Thread.Sleep(Timeout.Infinite) causes a thread to sleep until it is interrupted

by another thread that calls Thread.Interrupt. A thread can also be paused

by calling Thread.Suspend. When a thread calls Thread.Suspend on itself, the

call blocks until the thread is resumed by another thread. When one thread calls

Thread.Suspend on another thread, the call is a non-blocking call that causes the

118 CHAPTER 9. PROCESSES AND THREADS

other thread to pause.

The following program Threads0.cs is the simple example. We create a Thread

instance by passing it an object of ThreadStart delegate which contains a reference

to our ThreadJob method. Thus the code creates a new thread which runs the

ThreadJob method, and starts it. That thread counts from 0 to 9 fairly fast (about

twice a second) while the main thread counts from 0 to 4 fairly slowly (about once a

second). The way they count at different speeds is by each of them including a call

to Thread.Sleep(), which just makes the current thread sleep (do nothing) for the

specified period of time (milliseconds). Between each count in the main thread we

sleep for 1000ms, and between each count in the other thread we sleep for 500ms.

// ThreadsSleep.cs

using System;

using System.Threading;

public class ThreadsSleep

{

static void Main()

{

ThreadStart job = new ThreadStart(ThreadJob);

Thread thread = new Thread(job);

thread.Start();

int i = 0;

while(i < 5)

{

Console.WriteLine("Main thread: {0}",i);

Thread.Sleep(1000);

i++;

} // end while

} // end Main

static void ThreadJob()

{

int i = 0;

while(i < 10)

{

Console.WriteLine("Other thread: {0}",i);

Thread.Sleep(500);

i++;

} // end while

} // end ThreadJob

}

9.2. THREADS 119

A typical output could be

Main thread: 0

Other thread: 0

Other thread: 1

Main thread: 1

Other thread: 2

Other thread: 3

Other thread: 4

Main thread: 2

Other thread: 5

Main thread: 3

Other thread: 6

Other thread: 7

Main thread: 4

Other thread: 8

Other thread: 9

At another run this could change.

9.2.4 Join Methods

The method Join() blocks the calling thread until a thread terminates.

// joiningthread.cs

using System;

using System.Threading;

class JoiningThread

{

public static void Run()

{

for(int i=1;i<3;i++)

Console.WriteLine("Hello {0}",i);

}

public static void Main()

{

Thread t = new Thread(new ThreadStart(Run));

t.Start();

for(int i=1;i<6;i++)

Console.WriteLine("Welcome {0}",i);

t.Join();

Console.WriteLine("Goodbye");

}

120 CHAPTER 9. PROCESSES AND THREADS

}

The output is

Welcome 1

Welcome 2

Welcome 3

Welcome 4

Welcome 5

Hello 1

Hello 2

9.3 Monitor

While one thread is in a read/increment/write operation, no other threads can try

to do the same thing. This is where monitors come in. Every object in .NET has

a monitor associated with it. A thread can enter (or acquire) a monitor only if no

other thread has currently “got” it. Once a thread has acquired a monitor, it can

acquire it more times, or exit (or release) it. The monitor is only available to other

threads again once it has exited as many times as it was entered. If a thread tries

to acquire a monitor which is owened by another thread, it will block until it is able

to acquire it. There may be more than one thread trying to acquire the monitor,

in which case when the current owner thread releases it for the last time, only one

of the threads will acquire it - the other one will have to wait for the new owner to

release it too. The Pulse(object) method notifies a thread in the waiting queue

of a change in the locked object’s state. The method Wait(object) waits for the

Monitor Pulse.

// waitingthread.cs

using System;

using System.Threading;

class WaitingThread

{

static object obj = new object();

public static void thread1()

{

for(int i=1;i<6;i++)

Console.WriteLine("Welcome: {0}",i);

Monitor.Enter(obj);

Monitor.Pulse(obj);

Monitor.Exit(obj);

}

9.4. SYNCHRONIZATION 121

public static void thread2()

{

for(int i=1;i<15;i++)

Console.WriteLine("Hello: {0}",i);

Monitor.Enter(obj);

Monitor.Pulse(obj);

Monitor.Exit(obj);

}

public static void thread3()

{

for(int i=1;i<8;i++)

Console.WriteLine("Good Night: {0}",i);

Monitor.Enter(obj);

Monitor.Wait(obj);

Monitor.Pulse(obj);

Monitor.Exit(obj);

}

public static void Main()

{

ThreadStart job1 = new ThreadStart(thread1);

ThreadStart job2 = new ThreadStart(thread2);

ThreadStart job3 = new ThreadStart(thread3);

Thread t1 = new Thread(job1);

Thread t2 = new Thread(job2);

Thread t3 = new Thread(job3);

t1.Start();

t2.Start();

t3.Start();

}

}

If thread3 runs last after thread1 and thread2 have completed it freeze at

Good Night: 7

9.4 Synchronization

When two or more threads share a common resource access needs to be serialized in a

process called synchronization. Synchronization is done with the lock() operation.

The first program is without the lock operation. In the second program the lock

operation is introduced.

122 CHAPTER 9. PROCESSES AND THREADS

// syncthreads1.cs

using System;

using System.Threading;

class Account

{

private double balance = 5000;

public void Withdraw(double amount)

{

Console.WriteLine("WITHDRAWING {0}",amount);

if(amount > balance) throw new Exception("INSUFFICIENT FUNDS");

Thread.Sleep(10); // do other stuff

balance -= amount;

Console.WriteLine("BALANCE {0}",balance);

}

}

class SymnThread

{

static Account acc = new Account();

public static void Run()

{

acc.Withdraw(3000);

}

public static void Main()

{

new Thread(new ThreadStart(Run)).Start();

acc.Withdraw(3000);

}

}

Now we introduce the lock.

// syncthreads2.cs

using System;

using System.Threading;

class Account

{

private double balance = 5000;

9.5. DEADLOCK 123

public void Withdraw(double amount)

{

Console.WriteLine("WITHDRAWING {0}",amount);

lock(this)

{

if(amount > balance) throw new Exception("INSUFFICIENT FUNDS");

Thread.Sleep(10); // do other stuff

balance -= amount;

}

Console.WriteLine("BALANCE {0}",balance);

}

}

class SymnThread

{

static Account acc = new Account();

public static void Run()

{

acc.Withdraw(3000);

}

public static void Main()

{

new Thread(new ThreadStart(Run)).Start();

acc.Withdraw(3000);

}

}

9.5 Deadlock

The second major problem of multi-threading is that of deadlocks. Simply put, this

is when two threads each holds a monitor that the other one wants. Each blocks,

waiting for the monitor that it’s waiting for to be released - and so the monitors are

never released, and the application hangs (or at least those threads involved in the

deadlock hang). An example is given below.

// Deadlock.cs

using System;

using System.Threading;

public class Deadlock

{

124 CHAPTER 9. PROCESSES AND THREADS

static readonly object firstLock = new object();

static readonly object secondLock = new object();

static void ThreadJob()

{

Console.WriteLine("\t\t\t\tLocking firstLock");

lock(firstLock)

{

Console.WriteLine("\t\t\t\tLocked firstLock");

// Wait until we are fairly sure the first thread

// has grabbed secondlock

Thread.Sleep(1000);

Console.WriteLine("\t\t\t\tLocking secondLock");

lock(secondLock)

{

Console.WriteLine("\t\t\t\tLocked secondLock");

}

Console.WriteLine("\t\t\t\tReleased secondLock");

}

Console.WriteLine("\t\t\t\tReleased firstLock");

} // end method ThreadJob

public static void Main()

{

new Thread(new ThreadStart(ThreadJob)).Start();

// wait until we are fairly sure the other thread

// has grabbed firstlock

Thread.Sleep(500);

Console.WriteLine("Locking secondLock");

lock(secondLock)

{

Console.WriteLine("Locked secondLock");

Console.WriteLine("Locking firstLock");

lock(firstLock)

{

Console.WriteLine("Locked firstLock");

}

Console.WriteLine("Released firstLock");

}

Console.WriteLine("Released secondLock");

} // end Main

} // end class

9.6. INTERLOCKED CLASS 125

9.6 Interlocked Class

An operation is atomic if it is indivisible - in other words, nothing else can happen

in the middle. Thus with an atomic write, we cannot have another thread reading

the value half way through the write, and ending up “seeing” half of the old value

and half of the new value. Sinilarly, with an atomic read, we cannot have another

thread changing the value half way through the read, ending up with a value which

is neither the old nor the new value. For example, for a long (64 bits) on a 32 bit

machine, if one thread is changing the value from 0 to 0x0123456789ABCDEF, there

is no guarantee that another thread will not see the value as 0x0123456700000000

or 0x0000000089ABCDEF.

The Interlocked class provides a set of methods for performing atomic changes: exchanges

(optionally performing a comparison first), increments and decrements. The

Exchange and CompareExchange methods act on a variables of type int, object,

or float; the Increment and Decrement methods act on variables of type int and

long.

// interlocked.cs

using System;

using System.Threading;

public class MyInterlocked

{

static long count = 0;

public static void Main()

{

ThreadStart job = new ThreadStart(ThreadJob);

Thread thread = new Thread(job);

thread.Start();

for(long i=0;i<5;i++)

{

Interlocked.Increment(ref count);

Console.WriteLine("I am in for loop in main");

}

thread.Join();

Console.WriteLine("final count: {0}",count);

} // end Main

static void ThreadJob()

{

long i = 0;

126 CHAPTER 9. PROCESSES AND THREADS

while(i < 5)

{

Interlocked.Increment(ref count);

Console.WriteLine("I am in ThreadJob");

i++;

}

} // end ThreadJob

}

First the for loop in Main will run to the end and then the while loop will be done.

9.7 Thread Pooling

We can use thread pooling to make much more efficient use of multiple threads,

depending on our application. Many applications use multiple threads, but often

those threads spend a great deal of time in the sleeping state waiting for an event

to occur. Other threads might enter a sleeping state and be awakend only periodically

to poll for a change or update status information before going to sleep again.

Using thread pooling provides our application with a pool of worker threads that

are managed by the system, allowing us to concentrate on application tasks rather

than thread management. An example is given below.

// ThreadsSum2.cs

using System;

using System.Threading;

class ThreadsSum

{

public static void Sum1(Object o)

{

int sum1 = 0;

int[] a1 = (int[]) o;

for(int i=0;i

{

sum1 += a1[i];

}

Console.WriteLine("sum1 = " + sum1);

}

public static void Sum2(Object o)

{

int sum2 = 0;

int[] a2 = (int[]) o;

for(int i=0;i

9.8. THREADING IN WINDOWS FORMS 127

{

sum2 += a2[i];

}

Console.WriteLine("sum2 = " + sum2);

}

public static void Main()

{

int[] a1 = { 2, 3, 4 };

int[] a2 = { 4, 5, 7 };

if(ThreadPool.QueueUserWorkItem(new WaitCallback(Sum1),a1))

Console.WriteLine("Sum1 queued");

if(ThreadPool.QueueUserWorkItem(new WaitCallback(Sum2),a2))

Console.WriteLine("Sum2 queued");

Thread.Sleep(10); // Give other threads a turn

}

}

9.8 Threading in Windows Forms

How to handle threading in a UI? There are two rules for Windows Forms:

1) Never invoke any method or property on a control created on another thread other

than Invoke, BeginInvoke, EndInvoke or CreateGraphics, and InvokeRequired.

Each control is effectively bound to a thread which runs its message pump. If we

try to access or change anything in the UI (for example changing the Text property)

from a different thread, we run a risk of our program hanging or misbehaving

in other ways. We may get away with it in some cases. Fortunately, the Invoke,

BeginInvoke and EndInvoke methods have been provided so that we can ask the

UI thread to call a method in a safe manner.

2) Never execute a long-running piece of code in the UI thread. If our code is running

in the UI thread, that means no other code is running in that thread. That means

we won’t receive events, our controls won’t be repainted, etc. We can execute longrunning

code and periodically call Application.DoEvents(). It means we have

to consider re-entrancy issues etc, which are harder to diagnose and fix than ”normal”

threading problems. We have to judge when to call DoEvents, and we can’t

use anything which might block (network access, for instance) without risking an

unresponsive UI. There are message pumping issues in terms of COM objects as well.

If we have a piece of long-running code which we need to execute, we need to create

a new thread (or use a thread pool thread if we prefer) to execute it on, and make

128 CHAPTER 9. PROCESSES AND THREADS

sure it doesn’t directly try to update the UI with its results. The thread creation

part is the same as any other threading problem. It is interesting going the other

way - invoking a method on the UI thread in order to update the UI. There are two

different ways of invoking a method on the UI thread, one synchronous (Invoke)

and one asynchronous (BeginInvoke). They work in much the same way - we specify

a delegate and (optionally) some arguments, and a message goes on the queue

for the UI thread to process. If we use Invoke, the current thread will block until

the delegate has been executed. If we use BeginInvoke, the call will return immediately.

If we need to get the return value of a delegate invoked asynchronously, we

can use EndInvoke with the IAsyncResult returned by BeginInvoke to wait until

the delegate has completed and fetch the return value.

There are two options when working out how to get information between the various

threads involved. The first option is to have state in the class itself, setting it in

one thread, retrieving and processing it in the other (updating the display in the UI

thread, for example). The second option is to pass the information as parameters

in the delegate. Using state somewhere is necessary if we are creating a new thread

rather than using the thread pool - but that doesn’t mean we have to use state to

return information to the UI. However, creating a delegate with lots of parameters

feels clumsy, and is in some ways less efficient than using a simple MethodInvoker

or EventHandler delegate. These two delegates are treated in a special (fast) manner

by Invoke and BeginInvoke. MethodInvoker is just a delegate which takes no

parameters and returns no value (like ThreadStart), and EventHandler takes two

parameters (a sender and an EventArgs parameter and returns no value. However if

we pass an EventHandler delegate to Invoke or BeginInvoke then even if we specify

parameters ourself, they are ignored - when the method is invoked, the sender will

be the control we have invoked it with, and the EventArgs will be EventArgs.Empty.

Here is an example which shows several of the above concepts.

// ThreadingForms.cs

using System;

using System.Threading;

using System.Windows.Forms;

using System.Drawing;

public class Test : Form

{

delegate void StringParameterDelegate(string value);

Label statusIndicator;

Label counter;

Button button;

readonly object stateLock = new object();

9.8. THREADING IN WINDOWS FORMS 129

int target;

int currentCount;

Random rng = new Random();

Test()

{

Size = new Size(180,120);

Text = "Test";

Label lbl = new Label();

lbl.Text = "Status:";

lbl.Size = new Size(50,20);

lbl.Location = new Point(10,10);

Controls.Add(lbl);

lbl = new Label();

lbl.Text = "Count:";

lbl.Size = new Size(50,20);

lbl.Location = new Point(10,34);

Controls.Add(lbl);

statusIndicator = new Label();

statusIndicator.Size = new Size(100,20);

statusIndicator.Location = new Point(70,10);

Controls.Add(statusIndicator);

counter = new Label();

counter.Size = new Size(100,20);

counter.Location = new Point(70,34);

Controls.Add(counter);

button = new Button();

button.Text = "Run";

button.Size = new Size(50,20);

button.Location = new Point(10,58);

Controls.Add(button);

button.Click += new EventHandler(StartThread);

}

void StartThread(object sender,EventArgs e)

{

button.Enabled = false;

lock(stateLock)

{

130 CHAPTER 9. PROCESSES AND THREADS

target = rng.Next(100);

}

Thread t = new Thread(new ThreadStart(ThreadJob));

t.IsBackground = true;

t.Start();

}

void ThreadJob()

{

MethodInvoker updateCounterDelegate = new MethodInvoker(UpdateCount);

int localTarget;

lock(stateLock)

{

localTarget = target;

}

UpdateStatus("Starting");

lock(stateLock)

{

currentCount = 0;

}

Invoke(updateCounterDelegate);

// Pause before starting

Thread.Sleep(500);

UpdateStatus("Counting");

for(int i=0;i

{

lock(stateLock)

{

currentCount = i;

}

// Synchronously show the counter

Invoke(updateCounterDelegate);

Thread.Sleep(100);

}

UpdateStatus("Finished");

Invoke(new MethodInvoker(EnableButton));

}

void UpdateStatus(string value)

{

if(InvokeRequired)

{

// We’re not in the UI thread, so we need to call BeginInvoke

BeginInvoke(new StringParameterDelegate(UpdateStatus),new object[]{value});

9.8. THREADING IN WINDOWS FORMS 131

return;

}

// Must be on the UI thread if we’ve got this far

statusIndicator.Text = value;

}

void UpdateCount()

{

int tmpCount;

lock(stateLock)

{

tmpCount = currentCount;

}

counter.Text = tmpCount.ToString();

}

void EnableButton()

{

button.Enabled = true;

}

static void Main()

{

Application.Run(new Test());

}

}

State is used to tell the worker thread what number to count up to. A delegate taking

a parameter is used to ask the UI to update the status label. The worker thread’s

principal method actually just calls UpdateStatus, which uses InvokeRequired

to detect whether or not it needs to ”change thread”. If it does, it then calls

BeginInvoke to execute the same method again from the UI thread. This is quite

a common way of making a method which interacts with the UI thread-safe. The

choice of BeginInvoke rather than Invoke here was just to demonstrate how to

invoke a method asynchronously. In real code, we would decide based on whether

we needed to block to wait for the access to the UI to complete before continuing

or not. It is quite rare to actually require UI access to complete first, so we should

use BeginInvoke instead of Invoke. Another approach might be to have a property

which did the appropriate invoking when necessary. It is easier to use from the

client code, but slightly harder work in that we would either have to have another

method anyway, or get the MethodInfo for the property setter in order to construct

the delegate to invoke. In this case we actually know that BeginInvoke is required

because we are running in the worker thread anyway. We do not call EndInvoke after

the BeginInvoke. Unlike all other asynchronous methods we do not need to call

EndInvoke unless we need the return value of the delegate’s method. BeginInvoke

132 CHAPTER 9. PROCESSES AND THREADS

is also different to all of the other asynchronous methods as it doesn’t cause the

delegate to be run on a thread pool thread. State is used again to tell the UI thread

how far we have counted so far. We use a MethodInvoker delegate to execute

UpdateCount. We call this using Invoke to make sure that it executes on the UI

thread. This time there’s no attempt to detect whether or not an Invoke is required.

If we call BeginInvoke it will have a different effect than calling the method directly

as it will occur later, rather than in the current execution flow, of course. Again,

we actually know that we need to call Invoke here anyway. A button is provided to

let the user start the thread. It is disabled while the thread is running, and another

MethodInvoker delegate is used to enable the button again afterwards. All state

which is shared between threads (the current count and the target) is accessed in

locks in the way described earlier. We spend as little time as possible in the lock, not

updating the UI or anything else while holding the lock. This probably doesn’t make

too much difference here. It would be disastrous to still have the lock in the worker

thread when synchronously invoking UpdateCount - the UI thread would then try

to acquire the lock as well, and we end up with deadlock. The worker thread is

set to be a background thread (IsBackground=true;) so that when the UI thread

exits, the whole application finishes. In other cases where we have a thread which

should keep running even after the UI thread has quit, we need to be careful not

to call Invoke or BeginInvoke when the UI thread is no longer running - we will

either block permanently (waiting for the message to be taken off the queue, with

nothing actually looking at messages) or receive an exception.

9.9 Asynchronous Programming Model

When a caller invokes a method, the call is synchronous that is the caller has to

wait for the method to return before the remaining code can be executed. .NET has

an inbuilt support for asynchronous method invocation. Here the caller can issue a

request for invocation of a method and concurreently execute the remaining code.

For every delegate declared in an assembly the compiler emits a class (subclass of

System.MulticastDelegate) with Invoke, BeginInvoke and EndInvoke methods.

For example, consider a delegate declared as

delegate int MyWorker(char c,int m);

The compiler will emit the MyWorker class

class MyWorker : System.MulticastDelegate

{

public int Invoke(char c,int m);

public System.IAsyncResult BeginInvoke(char c,int m,System.AsyncCallback cb,

object asyncState);

public int EndInvoke(System.IAsyncResult result);

}

9.10. TIMERS 133

The BeginInvoke and EndInvoke methods can be used for asynchronous invocation

of a method pointed by MyWorker delegate. In the program below the method

DoWork (it displays character c m number of times) is invoked asynchronously with

character ’+’, in the next statement this method is directly invoked with character

’*’. Both ’+’ and ’*’ are displayed (1000 times each) on the Console simultaneously.

As an asynchronous call executes within its own background thread, we have

used Console.Read() to pause the main thread.

// Asynchronous.cs

using System;

delegate int MyWorker(char c,int m);

class AsncTest

{

static MyWorker worker;

static int DoWork(char c,int m)

{

int t = Environment.TickCount; // returns number of milliseconds

// elapsed since the system started

for(int i=1;i<=m;i++) Console.Write(c);

return (Environment.TickCount - t);

}

public static void Main()

{

Console.WriteLine("Start");

worker = new MyWorker(DoWork);

worker.BeginInvoke(’+’,1000,null,null); // asynchronous call

DoWork(’*’,1000); // synchronous call

Console.Read(); // pause until user presses a key

}

}

9.10 Timers

There are various different timers available in .NET, each of which basically calls

a delegate after a certain amount of time has passed. All the timers implement

IDispossible. so we have to make sure to dispose when we are not using them

anymore.

// Timers.cs

134 CHAPTER 9. PROCESSES AND THREADS

using System;

using System.Threading;

public class Timers

{

public static void Main()

{

Console.WriteLine("Started at {0:HH:mm:ss.fff}",DateTime.Now);

// Start in three seconds, then fire every second

using(Timer timer = new Timer(new TimerCallback(Tick),null,3000,1000))

{

// wait for 10 seconds

Thread.Sleep(10000);

// then go slow for another 10 seconds

timer.Change(0,2000);

Thread.Sleep(10000);

}

// the timerm will now have been disposed automatically due

// to the using statement so there won’t be any other threads running

} // end Main()

static void Tick(object state)

{

Console.WriteLine("Ticked at {0:HH:mm:ss.fff}",DateTime.Now);

}

}

9.11 Interrupt and Abort

There are two methods in the Thread class which are used for stopping threads -

Abort and Interrupt. Calling Thread.Abort aborts that thread as soon as possible.

Aborting a thread which is executing unmanaged code has no effect until the CLR

gets control again. Calling Thread.Interrupt is similar, but less drastic. This

causes a ThreadInterruptedException exception to be thrown the next time the

thread enters the WaitSleepJoin state, or immediately if the thread is already in

that state.

// Interruptthread.cs

using System;

using System.Threading;

class SleepingThread

{

public static void Run()

9.11. INTERRUPT AND ABORT 135

{

for(int i=1;i<6;i++)

Console.WriteLine("Welcome {0}",i);

try

{

Thread.Sleep(5000);

}

catch(ThreadInterruptedException e)

{

Console.WriteLine("Sleep Interrupted");

}

Console.WriteLine("Goodbye");

}

public static void Main()

{

Thread t = new Thread(new ThreadStart(Run));

t.Start();

for(int i=1;i<16;i++)

Console.WriteLine("Hello {0}",i);

t.Interrupt();

}

}

The program threadtest1.cs uses the Abort method.

// threadtest1.cs

using System;

using System.Threading; // for Thread class

class ThreadTest1

{

public static void SayHello()

{

for(int i=1;;i++)

{

Console.WriteLine("Hello {0}",i);

}

}

public static void Main()

{

Thread t = new Thread(new ThreadStart(SayHello));

136 CHAPTER 9. PROCESSES AND THREADS

t.Start();

for(int i=1;i<1001;i++)

{

Console.WriteLine("Bye {0}",i);

}

t.Abort();

}

}

No comments:

Post a Comment