Thursday, July 17, 2008

C# developer interview questions

Is it possible to inline assembly or IL in C# code? - No.

Is it possible to have different access modifiers on the get/set methods of a property? - No. The access modifier on a property applies to both its get and set accessors. What you need to do if you want them to be different is make the property read-only (by only providing a get accessor) and create a private/internal set method that is separate from the property.

Is it possible to have a static indexer in C#? - No. Static indexers are not allowed in C#.

If I return out of a try/finally in C#, does the code in the finally-clause run?

Yes. The code in the finally always runs. If you return out of the try block, or even if you do a “goto” out of the try, the finally block always runs:

Both “In Try block” and “In Finally block” will be displayed. Whether the return is in the try block or after the try-finally block, performance is not affected either way. The compiler treats it as if the return were outside the try block anyway. If it’s a return without an expression (as it is above), the IL emitted is identical whether the return is inside or outside of the try. If the return has an expression, there’s an extra store/load of the value of the expression (since it has to be computed within the try block).

I was trying to use an “out int” parameter in one of my functions. How should I declare the variable that I am passing to it?

- You should declare the variable as an int, but when you pass it in you must specify it as ‘out’, like the following: int i; foo(out i); where foo is declared as follows: [return-type] foo(out int o) { }

How does one compare strings in C#?

- In the past, you had to call .ToString() on the strings when using the == or != operators to compare the strings’ values. That will still work, but the C# compiler now automatically compares the values instead of the references when the == or != operators are used on string types. If you actually do want to compare references, it can be done as follows: if ((object) str1 == (object) str2) { … } Here’s an example showing how string compares work:

How do you specify a custom attribute for the entire assembly (rather than for a class)? - Global attributes must appear after any top-level using clauses and before the first type or namespace declarations. An example of this is as follows:

Note that in an IDE-created project, by convention, these attributes are placed in AssemblyInfo.cs.

How do I simulate optional parameters to COM calls?

- You must use the Missing class and pass Missing.Value (in System.Reflection) for any values that have optional parameters.

2) Which of these string definitions will prevent escaping on backslashes in C#?

1. string s = #.n Test string.;

2. string s = ..n Test string.;

3. string s = @.n Test string.;

4. string s = .n Test string.;

3) Which of these statements correctly declares a two-dimensional array in C#?

1. int[,] myArray;

2. int[][] myArray;

3. int[2] myArray;

4. System.Array[2] myArray;

4) If a method is marked as protected internal who can access it?

1. Classes that are both in the same assembly and derived from the declaring class.

2. Only methods that are in the same class as the method in question.

3. Internal methods can be only be called using reflection.

4. Classes within the same assembly, and classes derived from the declaring class.

5) What is boxing?

a) Encapsulating an object in a value type.

b) Encapsulating a copy of an object in a value type.

c) Encapsulating a value type in an object.

d) Encapsulating a copy of a value type in an object.

6) What compiler switch creates an xml file from the xml comments in the files in an assembly?

1. /text

2. /doc

3. /xml

4. /help

7) What is a satellite Assembly?

A peripheral assembly designed to monitor permissions requests from an application.

1. Any DLL file used by an EXE file.

2. An assembly containing localized resources for another assembly.

3. An assembly designed to alter the appearance or .skin. of an application.

8) What is a delegate?

1. A strongly typed function pointer.

2. A light weight thread or process that can call a single method.

3. A reference to an object in a different process.

4. An inter-process message channel.

9) How does assembly versioning in .NET prevent DLL Hell?

1. The runtime checks to see that only one version of an assembly is on the machine at any one time.

2. .NET allows assemblies to specify the name AND the version of any assemblies they need to run.

3. The compiler offers compile time checking for backward compatibility.

4. It doesn.t.

11) In the NUnit test framework, which attribute must adorn a test class in order for it to be picked up by the NUnit GUI?

1. TestAttribute

2. TestClassAttribute

3. TestFixtureAttribute

4. NUnitTestClassAttribute

12) Which of the following operations can you NOT perform on an ADO.NET DataSet?

1. A DataSet can be synchronised with the database.

2. A DataSet can be synchronised with a RecordSet.

3. A DataSet can be converted to XML.

4. You can infer the schema from a DataSet.

13) In Object Oriented Programming, how would you describe encapsulation?

1. The conversion of one type of object to another.

2. The runtime resolution of method calls.

3. The exposition of data . 4. The separation of interface and implementation .

Interview Questions C#

General Questions


1. Does C# support multiple-inheritance?
No, use interfaces instead.

2. Describe the accessibility modifier “protected internal”.
It is available to derived classes and classes within the same Assembly (and naturally from the base class it’s declared in).

3. What’s the top .NET class that everything is derived from? System.Object.

4. Can you store multiple data types in System. Array? No.

5. How can you sort the elements of the array in descending order?
By calling Sort() and then Reverse() methods.

6. What’s the C# equivalent of C++ catch (…), which was a catch-all statement for any possible exception?
A catch block that catches the exception of type System.Exception. You can also omit the parameter data type in this case and just write catch {}.

7. Can multiple catch blocks be executed?
No. Once the proper catch code fires off, the control is transferred to the finally block.

8. What is the role of the DataReader class in ADO.NET connections?
It returns a read-only dataset from the data source when the command is executed.

Class Questions:

0. How do you inherit from a class in C#?
Place a colon and then the name of the base class.

1. Can you prevent your class from being inherited by another class?
Yes. The keyword “sealed” will prevent the class from being inherited.

2. Can you allow a class to be inherited, but prevent the method from being over-ridden?
Yes. Just leave the class public and make the method sealed.

3. What’s an abstract class?
A class that cannot be instantiated. Abstract class is an inherited class that have the methods overridden.

4. When do you absolutely have to declare a class as abstract?

When at least one of the methods in the class is abstract.

5. What’s an interface class?
It’s an abstract class with public abstract methods all of which must be implemented in the inherited classes.

6. Why can’t you specify the accessibility modifier for methods inside the interface?
They all must be public. Therefore, to prevent you from getting the false impression that you have any freedom of choice, you are not allowed to specify any accessibility, it’s public by default.

7. Can you inherit multiple interfaces?Yes.

8. What’s the difference between an interface and abstract class?
In an interface no accessibility modifiers are allowed, which is ok in an abstract class.

9. What is the difference between a Struct and a Class?
Structs are value-type variables and are thus saved on the stack -> additional overhead but faster retrieval. Another difference is that structs CANNOT inherit. (Questions courtesy of Eyal)

Method and Property Questions

10. What’s the implicit name of the parameter that gets passed into the set method/property of a class?
Value. The data type of the value parameter is defined by whatever data type the property is declared as.

11. What does the keyword “virtual” declare for a method or property?
The method or property can be overridden.

12. How is method overriding different from method overloading?
When overriding a method, you change the behavior of the method for the derived class. Overloading a method simply involves having another method with the same name within the class.

13. Can you override private virtual methods?
No. Private methods are not accessible outside the class.

Original answer: No, moreover, you cannot access private methods in inherited classes, have to be protected in the base class to allow any sort of access.

14. What are the different ways a method can be overloaded?
Different parameter data types, different number of parameters, different order of parameters.

15. What’s a delegate?
A delegate object encapsulates a reference to a method.

16. What’s a multicast delegate?
It’s a delegate that points to and eventually fires off several methods.

17. What is a satellite assembly?
When you write a multilingual or multi-cultural application in .NET, and want to distribute the core application separately from the localized modules, the localized assemblies that modify the core application are called satellite assemblies.

18. What namespaces are necessary to create a localized application?
System.Globalization and System.Resources.

19. What is the difference between and XML documentation tag?
Single line code example and multiple-line code example.

20. Is XML case-sensitive? Yes.

Debugging and Testing Questions

21. What debugging tools come with the .NET SDK?

1. CorDBG – command-line debugger.

2. DbgCLR – graphic debugger. Visual Studio .NET uses the DbgCLR.

22. What does the “This” window show in the debugger?
It points to the object that’s pointed to by this reference. Object’s instance data is shown.

23. What does assert() method do?
In debug compilation, assert takes in a Boolean condition as a parameter, and shows the error dialog if the condition is false. The program proceeds without any interruption if the condition is true.

24. What’s the difference between the Debug class and Trace class?
Documentation looks the same. Use Debug class for debug builds, use Trace class for both debug and release builds.

25. How do you debug an ASP.NET Web application?
Attach the aspnet_wp.exe process to the DbgClr debugger.

26. What are three test cases you should go through in unit testing?

1. Positive test cases (correct data, correct output).

2. Negative test cases (broken or missing data, proper handling).

3. Exception test cases (exceptions are thrown and caught properly).

27. Can you change the value of a variable while debugging a C# application? Yes.

ADO.NET and Database Questions

1. What are advantages and disadvantages of Microsoft-provided data provider classes in ADO.NET?
SQLServer.NET data provider is high-speed and robust. OLE-DB.NET is universal for accessing other sources, like Oracle, DB2, Microsoft Access. OLE-DB.NET is not as fastest and efficient as SqlServer.NET.

2. Explain ACID rule of thumb for transactions.
A transaction must be:

1. Atomic - It is one unit of work and does not dependent on previous and following transactions.

2. Consistent - Data is either committed or roll back, no “in-between” case where something has been updated and something hasn’t.

3. Isolated - No transaction sees the intermediate results of the current transaction).

4. Durable - The values persist if the data committed even if the system crashes right after.

3. What connections does Microsoft SQL Server support?
Windows Authentication (via Active Directory) and SQL Server authentication (via Microsoft SQL Server username and password).

4. What does the Initial Catalog parameter define in the connection string?
The database name to connect to.

5. What is the data provider name to connect to an Access database?
Microsoft.Access.

6. What does the Dispose method do with the connection object?
Deletes it from the memory.

7. What is a pre-requisite for connection pooling?
Multiple processes must agree that they will share the same connection, where every parameter is the same, including the security settings. The connection string must be identical.

Some More C# Questions

1. When you inherit a protected class-level variable, who is it available to? Classes in the same namespace.

2. Are private class-level variables inherited? Yes, but they are not accessible, so looking at it you can honestly say that they are not inherited. But they are.

3. Describe the accessibility modifier protected internal. It’s available to derived classes and classes within the same Assembly (and naturally from the base class it’s declared in).

4. How’s method overriding different from overloading? When overriding, you change the method behavior for a derived class. Overloading simply involves having a method with the same name within the class.

5. What does the keyword virtual mean in the method definition? The method can be over-ridden.

6. Can you declare the override method static while the original method is non-static? No, you can’t, the signature of the virtual method must remain the same, only the keyword virtual is changed to keyword override.

7. How can you overload a method? By using parameters.

8. What’s the advantage of using System.Text.StringBuilder over System.String? StringBuilder is more efficient in the when lot of manipulations have to be done to the text.

9. How can you sort the elements of the array in descending order? By calling Sort() and then Reverse() methods.

10. What’s the .NET datatype that allows the retrieval of data by a unique key? HashTable.

11. What’s class SortedList underneath? A sorted HashTable.

12. Will finally block get executed if the exception had not occurred? Yes.

13. What’s the C# equivalent of C++ catch (…), which was a catch-all statement for any possible exception? A catch block that catches the exception of type System.Exception. You can also omit the parameter data type in this case and just write catch {}.

14. What’s a delegate? A delegate object encapsulates a reference to a method. In C++ they were referred to as function pointers.

15. What’s a multicast delegate? It’s a delegate that points to and eventually fires off several methods.

16. How’s the DLL Hell problem solved in .NET? Assembly versioning allows the application to specify not only the library it needs to run (which was available under Win32), but also the version of the assembly.

17. What’s a satellite assembly? When you write a multilingual or multi-cultural application in .NET, and want to distribute the core application separately from the localized modules, the localized assemblies that modify the core application are called satellite assemblies.

18. What namespaces are necessary to create a localized application? System.Globalization, System.Resources.

19. What’s the difference between and XML documentation tag? Single line code example and multiple-line code example.

20. What debugging tools come with the .NET SDK? CorDBG – command-line debugger, and DbgCLR – graphic debugger. Visual Studio .NET uses the DbgCLR. To use CorDbg, you must compile the original C# file using the /debug switch.

21. How do you debug an ASP.NET Web application? Attach the aspnet_wp.exe process to the DbgClr debugger.

22. Can you change the value of a variable while debugging a C# application? Yes.

23. What’s the role of the DataReader class in ADO.NET connections? It returns a read-only dataset from the data source when the command is executed.

24. Which one is trusted and which one is untrusted? Windows Authentication is trusted because the username and password are checked with the Active Directory; the SQL Server authentication is untrusted, since SQL Server is the only verifier participating in the transaction.

25. Why would you use untrusted verificaion? Web Services might use it, as well as non-Windows applications.

26. What’s the data provider name to connect to Access database? Microsoft.Access.

27. What does dispose method do with the connection object? Deletes it from the memory.

28. What is a pre-requisite for connection pooling? Multiple processes must agree that they will share the same connection, where every parameter is the same, including the security settings.

Tuesday, July 15, 2008

Sample chapter on Multithreading from C#: A Programmer's Introduction

12.1 Introduction

It would be nice if we could perform one action at a time and perform it well, but that is

usually difficult to do. The human body performs a great variety of operations in parallel

or, as we will say throughout this chapter, concurrently. Respiration, blood circulation and

digestion, for example, can occur concurrently. All the senses—sight, touch, smell, taste

and hearing—can occur at once. Computers, too, perform operations concurrently. It is

common for desktop personal computers to be compiling a program, sending a file to a

printer and receiving electronic mail messages over a network concurrently.

Ironically, most programming languages do not enable programmers to specify concurrent

activities. Rather, programming languages generally provide only a simple set of

control structures that enable programmers to perform one action at a time, proceeding to

the next action after the previous one has finished. Historically, the type of concurrency that

computers perform today generally has been implemented as operating system “primitives”

available only to highly experienced “systems programmers.”

The Ada programming language, developed by the United States Department of

Defense, made concurrency primitives widely available to defense contractors building

military command-and-control systems. However, Ada has not been widely used in universities

and commercial industry.

The .NET Framework Class Library makes concurrency primitives available to the

applications programmer. The programmer specifies that applications contain “threads of

execution,” each thread designating a portion of a program that may execute concurrently

with other threads—this capability is called multithreading. Multithreading is available to

all .NET programming languages, including C#, Visual Basic and Visual C++.

Software Engineering Observation 12.1

The .NET Framework Class Library includes multithreading capabilities in namespace

System.Threading. This encourages the use of multithreading among a larger part of

the applications-programming community. 12.1

We discuss many applications of concurrent programming. When programs download

large files, such as audio clips or video clips from the World Wide Web, users do not want

to wait until an entire clip downloads before starting the playback. To solve this problem,

we can put multiple threads to work—one thread downloads a clip, and another plays the

clip. These activities, or tasks, then may proceed concurrently. To avoid choppy playback,

we synchronize the threads so that the player thread does not begin until there is a sufficient

amount of the clip in memory to keep the player thread busy.

Another example of multithreading is C#’s automatic garbage collection. C and C++

place with the programmer the responsibility of reclaiming dynamically allocated memory.

Outline

12.1 Introduction

12.2 Thread States: Life Cycle of a Thread

12.3 Thread Priorities and Thread Scheduling

12.4 Summary

Chapter 12 Multithreading 409

C# provides a garbage-collector thread that reclaims dynamically allocated memory that

is no longer needed.

Performance Tip 12.1

One of the reasons for the popularity of C and C++ over the years was that their memorymanagement

techniques were more efficient than those of languages that used g*arbage collectors.

In fact, memory management in C# often is faster than in C or C++.1

12.1

Good Programming Practice 12.1

Set an object reference to null when the program no longer needs that object. This enables

the garbage collector to determine at the earliest possible moment that the object can be garbage

collected. If such an object has other references to it, that object cannot be collected.12.1

Writing multithreaded programs can be tricky. Although the human mind can perform

functions concurrently, people find it difficult to jump between parallel “trains of thought.”

To see why multithreading can be difficult to program and understand, try the following

experiment: Open three books to page 1 and try reading the books concurrently. Read a few

words from the first book, then read a few words from the second book, then read a few

words from the third book, then loop back and read the next few words from the first book,

etc. After this experiment, you will appreciate the challenges of multithreading—switching

between books, reading briefly, remembering your place in each book, moving the book

you are reading closer so you can see it, pushing books you are not reading aside—and

amidst all this chaos, trying to comprehend the content of the books!

Performance Tip 12.2

A problem with single-threaded applications is that lengthy activities must complete before

other activities can begin. In a multithreaded application, threads can share a processor (or

set of processors), so that multiple tasks are performed in parallel. 12.2

12.2 Thread States: Life Cycle of a Thread

At any time, a thread is said to be in one of several thread states (illustrated in Fig. 12.1)2.

This section discusses these states and the transitions between states. Two classes critical

for multithreaded applications are Thread and Monitor (System.Threading

namespace). This section also discusses several methods of classes Thread and Monitor

that cause state transitions.

A new thread begins its lifecyle in the Unstarted state. The thread remains in the

Unstarted state until the program calls Thread method Start, which places the thread

in the Started state (sometimes called the Ready or Runnable state) and immediately returns

control to the calling thread. Then the thread that invoked Start, the newly Started thread

and any other threads in the program execute concurrently.

1. E. Schanzer, “Performance Considerations for Run-Time Technologies in the .NET Framework,”

August 2001

/library/en-us/dndotnet/html/dotnetperftechs.asp>.

2. As this book went to publication, Microsoft changed the names of the Started and Blocked thread

states to Running and WaitSleepJoin, respectively.

410 Multithreading Chapter 12

The highest priority Started thread enters the Running state (i.e., begins executing)

when the operating system assigns a processor to the thread (Section 12.3 discusses thread

priorities). When a Started thread receives a processor for the first time and becomes a Running

thread, the thread executes its ThreadStart delegate, which specifies the actions

the thread will perform during its lifecyle. When a program creates a new Thread, the program

specifies the Thread’s ThreadStart delegate as the argument to the Thread

constructor. The ThreadStart delegate must be a method that returns void and takes

no arguments.

A Running thread enters the Stopped (or Dead) state when its ThreadStart delegate

terminates. Note that a program can force a thread into the Stopped state by calling

Thread method Abort on the appropriate Thread object. Method Abort throws a

ThreadAbortException in the thread, normally causing the thread to terminate.

When a thread is in the Stopped state and there are no references to the thread object, the

garbage collector can remove the thread object from memory.

A thread enters the Blocked state when the thread issues an input/output request. The

operating system blocks the thread from executing until the operating system can complete

the I/O for which the thread is waiting. At that point, the thread returns to the Started state, so

it can resume execution. A Blocked thread cannot use a processor even if one is available.

Fig. 12.1 Thread life cycle.

Started

Running

WaitSleepJoin Suspended Stopped Blocked

Unstarted

dispatch

(assign a

processor)

quantum

expiration

Start

I/O completion

Suspend Issue I/O request

Wait

Interrupt

sleep interval expires

Resume

Sleep, Join

Pulse

PulseAll

complete

Chapter 12 Multithreading 411

There are three ways in which a Running thread enters the WaitSleepJoin state. If a

thread encounters code that it cannot execute yet (normally because a condition is not satisfied),

the thread can call Monitor method Wait to enter the WaitSleepJoin state.

Once in this state, a thread returns to the Started state when another thread invokes Monitor

method Pulse or PulseAll. Method Pulse moves the next waiting thread

back to the Started state. Method PulseAll moves all waiting threads back to the

Started state.

A Running thread can call Thread method Sleep to enter the WaitSleepJoin state

for a period of milliseconds specified as the argument to Sleep. A sleeping thread returns

to the Started state when its designated sleep time expires. Sleeping threads cannot use a

processor, even if one is available.

Any thread that enters the WaitSleepJoin state by calling Monitor method Wait or

by calling Thread method Sleep also leaves the WaitSleepJoin state and returns to the

Started state if the sleeping or waiting Thread’s Interrupt method is called by another

thread in the program.

If a thread cannot continue executing (we will call this the dependent thread) unless

another thread terminates, the dependent thread calls the other thread’s Join method to

“join” the two threads. When two threads are “joined,” the dependent thread leaves the

WaitSleepJoin state when the other thread finishes execution (enters the Stopped state).

If a Running Thread’s Suspend method is called, the Running thread enters the Suspended

state. A Suspended thread returns to the Started state when another thread in the

program invokes the Suspended thread’s Resume method.

12.3 Thread Priorities and Thread Scheduling

Every thread has a priority in the range between ThreadPriority.Lowest to

ThreadPriority.Highest. These two values come from the ThreadPriority

enumeration (namespace System.Threading). The enumeration consists of the values

Lowest, BelowNormal, Normal, AboveNormal and Highest. By default, each

thread has priority Normal.

The Windows operating system supports a concept, called timeslicing, that enables

threads of equal priority to share a processor. Without timeslicing, each thread in a set of

equal-priority threads runs to completion (unless the thread leaves the Running state and

enters the WaitSleepJoin, Suspended or Blocked state) before the thread’s peers get a

chance to execute. With timeslicing, each thread receives a brief burst of processor time,

called a quantum, during which the thread can execute. At the completion of the quantum,

even if the thread has not finished executing, the processor is taken away from that thread

and given to the next thread of equal priority, if one is available.

The job of the thread scheduler is to keep the highest-priority thread running at all

times and, if there is more than one highest-priority thread, to ensure that all such threads

execute for a quantum in round-robin fashion (i.e., these threads can be timesliced).

Figure 12.2 illustrates the multilevel priority queue for threads. In Fig. 12.2, assuming a

single-processor computer, threads A and B each execute for a quantum in round-robin

fashion until both threads complete execution. This means that A gets a quantum of time

to run. Then B gets a quantum. Then A gets another quantum. Then B gets another

quantum. This continues until one thread completes. The processor then devotes all its

power to the thread that remains (unless another thread of that priority is Started). Next,

412 Multithreading Chapter 12

thread C runs to completion. Threads D, E and F each execute for a quantum in roundrobin

fashion until they all complete execution. This process continues until all threads

run to completion. Note that, depending on the operating system, new higher-priority

threads could postpone—possibly indefinitely—the execution of lower-priority threads.

Such indefinite postponement often is referred to more colorfully as starvation.

A thread’s priority can be adjusted with the Priority property, which accepts

values from the ThreadPriority enumeration. If the argument is not one of the valid

thread-priority constants, an ArgumentException occurs.

A thread executes until it dies, becomes Blocked for input/output (or some other

reason), calls Sleep, calls Monitor method Wait or Join, is preempted by a thread of

higher priority or has its quantum expire. A thread with a higher priority than the Running

thread can become Started (and hence preempt the Running thread) if a sleeping thread

wakes up, if I/O completes for a thread that Blocked for that I/O, if either Pulse or

PulseAll is called on an object on which Wait was called, or if a thread to which the

high-priority thread was Joined completes.

Figure 12.3 demonstrates basic threading techniques, including the construction of a

Thread object and using the Thread class’s static method Sleep. The program creates

three threads of execution, each with the default priority Normal. Each thread displays

a message indicating that it is going to sleep for a random interval of from 0 to 5000

milliseconds, then goes to sleep. When each thread awakens, the thread displays its name,

indicates that it is done sleeping, terminates and enters the Stopped state. You will see that

method Main (i.e., the Main thread of execution) terminates before the application terminates.

The program consists of two classes—ThreadTester (lines 8–41), which creates

the three threads, and MessagePrinter (lines 44–73), which defines a Print method

containing the actions each thread will perform.

Fig. 12.2 Thread-priority scheduling.

Priority Highest

Priority AboveNormal

Priority Normal

Priority BelowNormal

Priority Lowest

Ready threads

A B

C

D E F

G

Chapter 12 Multithreading 413

1 // Fig. 12.3: ThreadTester.cs

2 // Multiple threads printing at different intervals.

34

using System;

5 using System.Threading;

67

// class ThreadTester demonstrates basic threading concepts

8 class ThreadTester

9 {

10 static void Main( string[] args )

11 {

12 // Create and name each thread. Use MessagePrinter's

13 // Print method as argument to ThreadStart delegate.

14 MessagePrinter printer1 = new MessagePrinter();

15 Thread thread1 =

16 new Thread ( new ThreadStart( printer1.Print ) );

17 thread1.Name = "thread1";

18

19 MessagePrinter printer2 = new MessagePrinter();

20 Thread thread2 =

21 new Thread ( new ThreadStart( printer2.Print ) );

22 thread2.Name = "thread2";

23

24 MessagePrinter printer3 = new MessagePrinter();

25 Thread thread3 =

26 new Thread ( new ThreadStart( printer3.Print ) );

27 thread3.Name = "thread3";

28

29 Console.WriteLine( "Starting threads" );

30

31 // call each thread's Start method to place each

32 // thread in Started state

33 thread1.Start();

34 thread2.Start();

35 thread3.Start();

36

37 Console.WriteLine( "Threads started\n" );

38

39 } // end method Main

40 } // end class ThreadTester

41

42 // Print method of this class used to control threads

43 class MessagePrinter

44 {

45 private int sleepTime;

46 private static Random random = new Random();

47

48 // constructor to initialize a MessagePrinter object

49 public MessagePrinter()

50 {

51 // pick random sleep time between 0 and 5 seconds

52 sleepTime = random.Next( 5001 );

53 }

Fig. 12.3 Threads sleeping and printing. (Part 1 of 2)

414 Multithreading Chapter 12

Objects of class MessagePrinter (lines 44–73) control the lifecycle of each of the

three threads class ThreadTester’s Main method creates. Class MessagePrinter

consists of instance variable sleepTime (line 46), static variable random (line 47),

a constructor (lines 50–54) and a Print method (lines 57–71). Variable sleepTime

stores a random integer value chosen when a new MessagePrinter object’s constructor

is called. Each thread controlled by a MessagePrinter object sleeps for the amount of

time specified by the corresponding MessagePrinter object’s sleepTime

54

55 // method Print controls thread that prints messages

56 public void Print()

57 {

58 // obtain reference to currently executing thread

59 Thread current = Thread.CurrentThread;

60

61 // put thread to sleep for sleepTime amount of time

62 Console.WriteLine(

63 current.Name + " going to sleep for " + sleepTime );

64

65 Thread.Sleep ( sleepTime );

66

67 // print thread name

68 Console.WriteLine( current.Name + " done sleeping" );

69

70 } // end method Print

71

72 } // end class MessagePrinter

Starting threads

Threads started

thread1 going to sleep for 1977

thread2 going to sleep for 4513

thread3 going to sleep for 1261

thread3 done sleeping

thread1 done sleeping

thread2 done sleeping

Starting threads

Threads started

thread1 going to sleep for 1466

thread2 going to sleep for 4245

thread3 going to sleep for 1929

thread1 done sleeping

thread3 done sleeping

thread2 done sleeping

Fig. 12.3 Threads sleeping and printing. (Part 2 of 2)

Chapter 12 Multithreading 415

The MessagePrinter constructor (lines 50–54) initializes sleepTime to a

random integer from 0 up to, but not including, 5001 (i.e., from 0 to 5000).

Method Print begins by obtaining a reference to the currently executing thread (line

60) via class Thread’s static property CurrentThread. The currently executing

thread is the one that invokes method Print. Next, lines 63–64 display a message indicating

the name of the currently executing thread and stating that the thread is going to sleep

for a certain number of milliseconds. Note that line 64 uses the currently executing thread’s

Name property to obtain the thread’s name (set in method Main when each thread is created).

Line 66 invokes static Thread method Sleep to place the thread into the Wait-

SleepJoin state. At this point, the thread loses the processor and the system allows another

thread to execute. When the thread awakens, it reenters the Started state again until the

system assigns a processor to the thread. When the MessagePrinter object enters the

Running state again, line 69 outputs the thread’s name in a message that indicates the thread

is done sleeping, and method Print terminates.

Class ThreadTester’s Main method (lines 10–39) creates three objects of class

MessagePrinter, at lines 14, 19 and 24, respectively. Lines 15–16, 20–21 and 25–26

create and initialize three Thread objects. Lines 17, 22 and 27 set each Thread’s Name

property, which we use for output purposes. Note that each Thread’s constructor receives

a ThreadStart delegate as an argument. Remember that a ThreadStart delegate

specifies the actions a thread performs during its lifecyle. Line 16 specifies that the delegate

for thread1 will be method Print of the object to which printer1 refers. When

thread1 enters the Running state for the first time, thread1 will invoke printer1’s

Print method to perform the tasks specified in method Print’s body. Thus, thread1

will print its name, display the amount of time for which it will go to sleep, sleep for that

amount of time, wake up and display a message indicating that the thread is done sleeping.

At that point method Print will terminate. A thread completes its task when the method

specified by a Thread’s ThreadStart delegate terminates, placing the thread in the

Stopped state. When thread2 and thread3 enter the Running state for the first time,

they invoke the Print methods of printer2 and printer3, respectively. Threads

thread2 and thread3 perform the same tasks as thread1 by executing the Print

methods of the objects to which printer2 and printer3 refer (each of which has its

own randomly chosen sleep time).

Testing and Debugging Tip 12.1

Naming threads helps in the debugging of a multithreaded program. Visual Studio .NET’s

debugger provides a Threads window that displays the name of each thread and enables

you to view the execution of any thread in the program. 12.1

Lines 33–35 invoke each Thread’s Start method to place the threads in the Started

state (sometimes called launching a thread). Method Start returns immediately from

each invocation, then line 37 outputs a message indicating that the threads were started, and

the Main thread of execution terminates. The program itself does not terminate, however,

because there are still threads that are alive (i.e., the threads were Started and have not

reached the Stopped state yet). The program will not terminate until its last thread dies.

When the system assigns a processor to a thread, the thread enters the Running state and

calls the method specified by the thread’s ThreadStart delegate. In this program, each

thread invokes method Print of the appropriate MessagePrinter object to perform

the tasks discussed previously.

416 Multithreading Chapter 12

Note that the sample outputs for this program show each thread and the thread’s

sleep time as the thread goes to sleep. The thread with the shortest sleep time normally

awakens first, indicates that it is done sleeping and terminates.

12.4 Summary

Computers perform multiple operations concurrently. Programming languages generally

provide only a simple set of control structures that enable programmers to perform just one

action at a time and proceed to the next action only after the previous one finishes. The

FCL, however, provides the C# programmer with the ability to specify that applications

contain threads of execution, where each thread designates a portion of a program that may

execute concurrently with other threads. This capability is called multithreading.

A thread is initialized using the Thread class’s constructor, which receives a

ThreadStart delegate. This delegate specifies the method that contains the tasks a

thread will perform. A thread remains in the Unstarted state until the thread’s Start

method is called, which the thread enters the Started state. A thread in the Started state

enters the Running state when the system assigns a processor to the thread. The system

assigns the processor to the highest-priority Started thread. A thread enters the Stopped

state when its ThreadStart delegate completes or terminates. A thread is forced into the

Stopped state when its Abort method is called (by itself or by another thread). A Running

thread enters the Blocked state when the thread issues an input/output request. A Blocked

thread becomes Started when the I/O it is waiting for completes. A Blocked thread cannot

use a processor, even if one is available.

If a thread needs to sleep, it calls method Sleep. A thread wakes up when the designated

sleep interval expires. If a thread cannot continue executing unless another thread terminates,

the first thread, referred to as the dependent thread, calls the other thread’s Join

method to “join” the two threads. When two threads are joined, the dependent thread leaves

the WaitSleepJoin state when the other thread finishes execution.

When a thread encounters code that it cannot yet run, the thread can call Monitor

method Wait until certain actions occur that enable the thread to continue executing. This

method call puts the thread into the WaitSleepJoin state. Any thread in the WaitSleepJoin

state can leave that state if another thread invokes Thread method Interrupt on the

thread in the WaitSleepJoin state. If a thread has called Monitor method Wait, a corresponding

call to Monitor method Pulse or PulseAll by another thread in the program

will transition the original thread from the WaitSleepJoin state to the Started state.

If Thread method Suspend is called on a thread, the thread enters the Suspended

state. A thread leaves the Suspended state when a separate thread invokes Thread method

Resume on the suspended thread.

Every C# thread has a priority. The job of the thread scheduler is to keep the highestpriority

thread running at all times and, if there is more than one highest-priority thread, to

ensure that all equally high-priority threads execute for a quantum at a time in round-robin

fashion. A thread’s priority can be adjusted with the Priority property, which is

assigned an argument from the ThreadPriority enumeration.