Do the right things and do the things right.

Menu

[EN] Discovering System.Threading namespace, the Thread class

This post is a part of preparation for 70-483 exam. Information written down here refers to the part Manage program flow.

.NET framework provides several mechanisms to write concurrency, parallel and asynchronous code. They all are included in System.Threading namespace. This namespace contains types that allow creating multithreaded applications. Today’s post is about the Thread class.

This topic is not required for the exam. However, the exam preparation materials sometimes references to it and I decided to write it down.

The Thread class is located in the System.Threading namespace and it has been a part of the .NET framework since version 1.1. It gives an ability to create new treads, manage their priority, get their status, tell Windows that the thread is long running, or configure other advanced options. In general using the Thread class you have control over all configuration options.

Basic usage of the Thread class

The code below is an example of an application using Thread class.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

usingSystem;

usingSystem.Threading;

Threadt=newThread(()=>{

Console.WriteLine("Additional thread started.");

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

{

Console.WriteLine("Additional thread is counting: {0}",i);

Thread.Sleep(1000);

}

});

t.Start();

for(inti=0;i<7;i++)

{

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

Thread.Sleep(500);

}

t.Join();

Console.WriteLine("Additional thread returned");

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

//Main thread is counting: 0

//Additional thread started.

//Additional thread is counting: 0

//Main thread is counting: 1

//Main thread is counting: 2

//Additional thread is counting: 1

//Main thread is counting: 3

//Main thread is counting: 4

//Additional thread is counting: 2

//Main thread is counting: 5

//Additional thread is counting: 3

//Main thread is counting: 6

//Additional thread is counting: 4

//Additional thread returned

The output shows that those two threads run concurrently. I passed a lambda expression to the Thread class constructor but thread can be initialized within ThreadStart delegate as well.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

usingSystem;

usingSystem.Threading;

publicstaticclassWorker{

publicstaticvoidTodo(){

Console.WriteLine("Additional thread started.");

for(inti=0;i<2;i++)

{

Console.WriteLine("Additional thread is counting: {0}",i);

Thread.Sleep(1000);

}

}

}

Threadt=newThread(newThreadStart(Worker.Todo));

t.Start();

for(inti=0;i<3;i++)

{

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

Thread.Sleep(500);

}

t.Join();

Console.WriteLine("Additional threads returned.");

Calling Start() method on a thread object runs the thread. This method has an overloaded version which takes object parameter and passes it to the thread. It’s useful when you want to pass additional data to the thread at start time.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

usingSystem;

usingSystem.Threading;

publicstaticclassWorker{

publicstaticvoidTodo(objectmax){

Console.WriteLine("Additional thread started.");

for(inti=0;i<(int)max;i++)

{

Console.WriteLine("Additional thread is counting: {0}",i);

Thread.Sleep(1000);

}

}

}

Threadt=newThread((max)=>{

Console.WriteLine("Another additional thread started.");

for(inti=0;i<(int)max;i++)

{

Console.WriteLine("Another additional thread is counting: {0}",i);

Thread.Sleep(1000);

}

});

Thread t2=newThread(newParameterizedThreadStart(Worker.Todo));

t.Start(4);

t2.Start(2);

for(inti=0;i<3;i++)

{

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

Thread.Sleep(500);

}

t.Join();

t2.Join();

Console.WriteLine("Additional threads returned.");

At the bottom of each example you can notice Join() method call. This method blocks the calling thread to wait till the thread finishes execution.

Stopping the thread

Thread class contains an Abort method which raises a ThreadAbortException on the target thread. It is not the best choice because it can potentially leave a corrupt state and make your application unusable. Using a shared variable that both your target and your calling thread can access is an alternative to stop a thread. The example below shows that concept.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

usingSystem;

usingSystem.Threading;

boolstarted=true;

Threadt=newThread(()=>{

Console.WriteLine("Additional thread started.");

inti;

while(started)

{

Console.WriteLine("Additional thread is counting: {0}",i++);

Thread.Sleep(1000);

}

});

t.Start();

for(inti=0;i<7;i++)

{

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

Thread.Sleep(500);

}

started=false;

t.Join();

Console.WriteLine("Additional thread returned");

ThreadStaticAttribute and ThreadLocal

Sometimes it’s reasonable to use static fields in the code. However, if the code is executed in multiple threads you can run into a problem where the field is influenced by each thread separately and it can lead you to unexpected troubles. Look at code above.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

using System;

using System.Threading;

staticintsum;

Threadt=newThread(()=>{

Console.WriteLine("Additional thread started.");

for(inti=0;i<3;i++)

{

Console.WriteLine("Additional thread is counting: {0}",i);

sum+=i;

Thread.Sleep(1000);

}

Console.WriteLine("Additional thread result is: {0}",sum);

});

t.Start();

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

{

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

Thread.Sleep(500);

sum+=i;

}

Console.WriteLine("Main thread result is: {0}",sum);

t.Join();

Console.WriteLine("Additional thread returned");

C#

1

2

3

4

5

6

7

8

9

10

11

12

//Main thread is counting: 0

//Additional thread started.

//Additional thread is counting: 0

//Main thread is counting: 1

//Main thread is counting: 2

//Additional thread is counting: 1

//Main thread is counting: 3

//Additional thread is counting: 2

//Main thread is counting: 4

//Main thread result is: 13

//Additional thread result is: 13

//Additional thread returned

The correct result should be 6 for the additional thread and 13 for the main thread. Instead both results are the same. Using ThreadStaticAttribute fixes this issue. This annotation gives each thread its unique copy of the field.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

usingSystem;

usingSystem.Threading;

[ThreadStatic]

staticintsum;

Threadt=newThread(()=>{

Console.WriteLine("Additional thread started.");

for(inti=0;i<3;i++)

{

Console.WriteLine("Additional thread is counting: {0}",i);

sum+=i;

Thread.Sleep(1000);

}

Console.WriteLine("Additional thread result is: {0}",sum);

});

t.Start();

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

{

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

Thread.Sleep(500);

sum+=i;

}

Console.WriteLine("Main thread result is: {0}",sum);

t.Join();

Console.WriteLine("Additional thread returned");

Unfortunately, when you want to initialize static field locally in each thread then ThreadStaticAttribute is not enough. Code like this:

C#

1

2

[ThreadStatic]

staticintsum=10;

Will set sum field to 10 only for the current thread, the others will still have 0. ThreadLocal class deals with that problem. It takes a delegate as a parameter and initializes the value.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

usingSystem;

usingSystem.Threading;

staticThreadLocal<int>sum=newThreadLocal<int>(()=>10);

Threadt=newThread(()=>{

Console.WriteLine("Additional thread started.");

for(inti=0;i<3;i++)

{

Console.WriteLine("Additional thread is counting: {0}",i);

sum.Value+=i;

Thread.Sleep(1000);

}

Console.WriteLine("Additional thread result is: {0}",sum);

});

t.Start();

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

{

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

Thread.Sleep(500);

sum.Value+=i;

}

Console.WriteLine("Main thread result is: {0}",sum);

t.Join();

Console.WriteLine("Additional thread returned");

IsBackground property

One other thing worth to mention is that thread can be run either as a foreground or a background thread. There is only a slight difference, as MSDN portal says:

Background threads are identical to foreground threads with one exception: a background thread does not keep the managed execution environment running. Once all foreground threads have been stopped in a managed process (where the .exe file is a managed assembly), the system stops all background threads and shuts down.

Thread object has IsBackground property which defines whether the thread is a background or a foreground thread. The example below shows the usage of this property.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

usingSystem;

usingSystem.Threading;

Threadt=newThread(()=>{

Console.WriteLine("Additional thread started.");

for(inti=0;i<10;i++)

{

Console.WriteLine("Additional thread is counting: {0}",i);

Thread.Sleep(1000);

}

});

t.IsBackground=true;

t.Start();

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

{

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

Thread.Sleep(500);

}

Console.WriteLine("Program finished");

The difference is that the program finishes when the main thread ends its execution and thread t1 is terminated immediately. If the flag would be set to false (default value) then the thread t1 would count to 10.

Summary

That’s all for today. I hope the topic is covered well enough. In further posts I will focus on Thread Pool class and Task Parallel Library. Stay tuned.