Thursday, September 06, 2007

Bringing The Background To The Foreground

One point to make on multi-threading applications if I have not done a good job of making it clear is that it should be done with a good deal of responsibility with regarding to synchronization or protection against threads colliding with each other, exception handling, and resource management.

With the discussion on using thread pool or normal threads mentioned at the end of my last post, I realized I used the term "background" threads a good deal, but did not show how to implement one or what the difference was between a thread running in the background according to my definition and a background thread as implemented in programming language.

A thread is either a background thread or a foreground thread. Background threads are identical to foreground threads, except that background threads do not prevent a process from terminating. Once all foreground threads belonging to a process have terminated, the common language run-time ends the process. Any remaining background threads are stopped and do not complete. -MSDN

Threads that belong to the managed thread pool (that is, threads whose IsThreadPoolThread property is true) are background threads. All threads that enter the managed execution environment from unmanaged code are marked as background threads. All threads generated by creating and starting a new Thread object are by default foreground threads. -MSDN


If you follow the first link above, the MSDN library entry has the following code for illustrating the difference in how the background and foreground threads are treated at termination of application.

Imports System
Imports System.Threading

Public Class Test

<MTAThread> _
Shared Sub Main()
Dim shortTest As New BackgroundTest(10)
Dim foregroundThread As New Thread(AddressOf shortTest.RunLoop)
foregroundThread.Name = "ForegroundThread"

Dim longTest As New BackgroundTest(50)
Dim backgroundThread As New Thread(AddressOf longTest.RunLoop)
backgroundThread.Name = "BackgroundThread"
backgroundThread.IsBackground = True

foregroundThread.Start()
backgroundThread.Start()
End Sub

End Class

Public Class BackgroundTest

Dim maxIterations As Integer

Sub New(maximumIterations As Integer)
maxIterations = maximumIterations
End Sub

Sub RunLoop()
Dim threadName As String = Thread.CurrentThread.Name

For i As Integer = 0 To maxIterations
Console.WriteLine("{0} count: {1}", _
threadName, i.ToString())
Thread.Sleep(250)
Next i

Console.WriteLine("{0} finished counting.", threadName)
End Sub

End Class

As stated previously, I have found "background" threads really useful for statistical analysis/logging activities that are not necessary for functionality of user request, but need data from request to get started. So the user doesn't wait for long time for a response, the application calculates results for user while spawning a background thread for performing business logic, file I/O, database calls, etc. necessary.

This works well keeping in mind whether or not it is important to you to maintain any statistical threads still running at application shutdown. With .NET typically being hosted on a Microsoft IIS server where application pools cycle periodically, the need to use foreground thread or programmatic handling of background threads on application end (global.asax) may be more prevalent.

I will try to be careful on how I use the term "background" as I tend to use this to refer to any thread that does not need to run while a user is waiting/watching.

Anyway, just a little tidbit of information to add to the arsenal.

1 comment:

kccjr2 said...

Using Thread.BeginCriticalRegion you can signal that a portion of code is about to execute that if aborted unexpectedly or has an unhandled exception will negatively impact the application as a whole. Additionally, the Thread.BeginThreadAffinity can called to indicate that code is about to execute depending on the identity of the current thread.

Both of these methods have complementary Thread.EndXXX routines that indicate end of block of code that fit each case.

Depending on application need, these methods may be helpful in controlling which threads or more granularly blocks of code need to be gracefully shutdown on application termination.