|
Thread ProgrammingThis page compares how to program Java and .NET computer programs to use threads for concurrency within the same process. This is needed for higher performance and better scalability. The "Mondrian" applet shown in this page uses the old Java 1.2 threading model. |
|
|
Thread Hazards
|
Java Threading
Python programs on multi-CPU machines have to divide the work up between multiple processes rather than multiple threads because (almost) all Python code can only run while it holds the Global Interpreter Lock (GIL). Thus, a multi-threaded Python program effectively only uses one CPU. However, the Jython interpreter can make use of Java's threading mechanisms. MS .NET ThreadingMicrosoft's .NET provides two types of multi-tasking in its namespace (library) System.Threading.
|
Thread States and Classes
A 6-state thread model was introduced by Java 1.3:
Previously in Java 1.2, a Thread can be in one of four states:
|
MS .NET Thread CreationWith C# .NET, another thread is created from within the main thread by instantiating a delegate object:
// to name a delegate: ThreadStart ts = new ThreadStart( MyThreadMethod ); Thread t = new Thread( ts ); // to create thread object. t.Start(); // to start the new thread. "MyThreadMethod" is the entryPoint name of the method called to begin thread execution, defined by code such as: public static void MyThreadMethod() { } Such entry point methods need to be defined as void because no arguments are passed to it. Also, calling Start() on a thread that has already been started will result in a ThreadStateException being thrown. Once started, the thread runs until the method specified by entryPoint returns and automatically stops execution. Example from Introducing Multithreaded Programming in C# Java Thread CreationTo implement multithreading in Java code, a class must do one of the following:
C Thread CreationTo create a new thread C code:
|
Yielding a ThreadTo switch the currently running thread to a runnable state so that a thread of the same priority has a chance to run, use the Java static method
Thread.yield(); |
Join the Line and WaitingJava provides the join() method on the thread object the program waits on. An example of its use (with timeout, from Strakt) in Jython: import threading import time class Sleeper(threading.Thread): def __init__(self, name, seconds): self.name = name self.seconds = seconds threading.Thread.__init__(self) def run(self): # while not done loop goes here in a real pgm. time.sleep(self.seconds) # for demo purposes. print '%s done' % self.name c1 = Sleeper('Long', 6) c1.setDaemon(1) c1.start() c2 = Sleeper('Short', 2) c2.start() c1.join(4) # with 4 second time-out print 'Done waiting for c1' The setDaemon method defines a thread object as a daemon. The best practice recommended is using the Runnable interface of the Java Thread object which offers thread management APIs for checking status, setting daemon thread status, and killing a thread. |
Putting it to Sleep
Thread.sleep(milliseconds); Thread.sleep(milliseconds, nanoseconds); C# .NET uses delegates to control threads. A delegate is like naming a signature of a function. Here is an example of using WaitCallBack to put the main section to sleep:
AutoResetEvent asyncOpIsDone = new AutoResetEvent(false);
ThreadPool.QueueUserWorkItem( new WaitCallback(MyAsyncOperation), asyncOpIsDone); // ... asyncOpIsDone.WaitOne(); // to do the waiting. The waiting is done by an async operation created by a method defined with the "Object state" signature required by WaitCallBack:
static void MyAsyncOperation(Object state){
((AutoResetEvent)state).Set(); // to signal async operation completion. C# .NET offers a convenient way to wake up every 2 seconds:
Timer timer = new Timer(
new
TimerCallback(CheckStatus), null, 0, 2000);
The CheckStatus method must also be defined with the "Object state" signature required by Timer:
static void CheckStatus(Object state){
|
VM to OS Thread Mapping
With the Blackdown port of the JDK 1.2 to Linux, a thread is akin to a process (one-to-one mapping).
With C# .NET, static variables used within a thread (and not for other threads) are defined thus:
[ThreadStatic] static Int32 tsValue = 10; |
$25 Taming Java Threads by Allen Holub (JavaWorld magazine writer). APress, June 2000. 300 pages. Includes semaphores and read/write locks with timeouts. $40 Concurrent Programming in Java : Design Principles and Patterns by Doug Lea. Addison-Wesley, November 1996. 339 pages. Describes Java 1.2.
|
Thread Scheduler Priorities
Thread settings can give hints to the thread scheduler, but do not guarantee a specific behavior. Higher-level interrupts will be serviced faster than lower-priority interrupts. It is a common misconception that one can:
Priority settings are ineffective because they impact work over a longer period of time than the microseconds granularity where race conditions occur. Setting priorities may not always have the desired effect because prioritization schemes may be implemented differently on different platforms. Tweaking priorities only seem to fix race conditions because that only makes the problem occur less frequently, but they also make the underlying problems more difficult to detect, recreate, and debug. Such fixes result in applications passing through QA, then deadlocking in the field.
.NET Fiver.NET program Priority is an enumeration of just 5 values:
SolarisWith the 15 Solaris priority levels, Process Interrupt Levels (PILs) or serial interrupts hold the five highest priority levels which block all lower-priority interrupts until they complete.Below a priority level of 10, interrupts are handled by threads. | Java's 1 - 5 - 10A Java thread's priority is specified with an integer from (the lowest) 1 to 10 (the highest), constants Thread.MIN_PRIORITY and Thread.MAX_PRIORITY. By default, the setPriority method sets a thread's priority to a value of 5 — the Thread.NORM_PRIORITYBut if you can't resist messing with it, increment:
Thread t = Thread.currentThread();
int intCurrentPriority; intCurrentPriority = t.getPriority(); t.setPriority( intCurrentPriority+1 ); Default priorities for threads started by Java:
|
Java Coding Runnable Sample
public class MyThread implements Runnable { private String holdA = "This is "; private int[] holdB = {1,2,3,4,5,6,7,8,9,10}; public static void main(String args[]) { MyThread z = new MyThread(); ( new Thread(z) ).start(); ( new Thread(z) ).start(); } // Any object implementing a synchronized keyword is a monitor: public synchronized void run() { for(int w = 0;w < 10;w++) { System.out.println(holdA + holdB[w] + "."); } } } |
Synchronized Monitor Threads
Thread MonitorsTo prevent problems that could occur by having two methods modifying the same object, Java uses monitors and the synchronized keyword to control access to an object by a thread. Any object that implements the "synchronized" keyword is considered to be a monitor. A monitor is an object that can move a thread process between a blocked and running state. Monitors are required when a process needs to wait for an external event to occur before thread processing can continue. A typical example is when a thread can't continue until an object reaches a certain state.Every object has a lock associated with it that controls access to code marked with the synchronized keyword. The wait method is used to move an object into a waiting state where it waits for a lock to be released. The notify and notifyAll methods are used to move a thread out of the waiting state and into the ready-to-run state. A thread acquires an exclusive lock on a shared resource by entering a waiting monitor object. When the thread is done, it calls a wait method. The mutually exclusive (mutex) locking mechanism waits are synchronized, which serves as a “token” or lock. It is recommended that threads monitor their execution and stop by returning from their run() method. A thread should monitor the state of a shared variable (interface events) to determine if its execution should be stopped, suspended, or resumed. The wait() and notify() methods of the Object class should be used to cause a thread to wait on changes to the value of a shared variable.
| $40 Programming with Hyper-Threading Technology: How to Write Multithreaded Software for Intel IA-32 Processors (Intel Press, 2004, 232 pages) by Richard Gerber and Andrew Binstock
|
Java Volatile ModifierUse the keyword volatile modifier on member variables if
|
Stopping Threads
do { // code ... } while (myThread1.thread.IsAlive && myThread2.thread.IsAlive && myThread3.thread.IsAlive ); The Microsoft .NET Framework also provides the Join() method to make threads wait until another thread terminates. A maximum amount of wait time can be specified. A ThreadStateException will be thrown if the thread has not been started. |
Thread-Safe Synchronized Sequential Execution
Paul Hyde, author of Java Thread Programming recommends that multiple threads and safely interact with each other by going though a “StopLight” class for threads to query before taking action. It hides Java wait/notify mechanisms and enforces timeouts. The helper class uses three objects for locking and inter-thread communication:
|
Inter-Thread Communications
public static bool Wait(object waitObject)or public static bool Wait(object waitObject, int milliseconds)
This is thread sychronization. Visual Basic.NET uses the "SyncLock" keyword. The waiting thread is awakened when some other thread enters the same lock and calls Monitor class methods
// Using Wait() and Pulse() to create a bouncing ball. // thread ping and thread pong. using System; using System.Threading; class PingPong { public void ping( bool running ) { lock(this) { if(!running) { //ball halts. Monitor.Pulse(this); // notify any waiting threads return; } Console.Write("Ping "); Monitor.Pulse(this); // let pong() run Monitor.Wait(this); // wait for pong() to complete } } public void pong( bool running ) { lock(this) { if(!running) { //ball halts. Monitor.Pulse(this); // notify any waiting threads return; } Console.WriteLine("Pong "); Monitor.Pulse(this); // let ping() run Monitor.Wait(this); // wait for ping() to complete } } } class MyThread { public Thread thread; PingPong pingpongObject; //construct a new thread. public MyThread(string name, PingPong pp) { thread = new Thread(new ThreadStart(this.run)); pingpongObject = pp; thread.Name = name; thread.Start(); } //Begin execution of new thread. void run() { if (thread.Name == "Ping") { for (int i = 0; i < 5; i++) pingpongObject.ping(true); pingpongObject.ping(false); } else { for (int i = 0; i < 5; i++) pingpongObject.pong(true); pingpongObject.pong(false); } } } class BouncingBall { public static void Main() { PingPong pp = new PingPong(); Console.WriteLine("The Ball is dropped... \n"); MyThread mythread1 = new MyThread("Ping", pp); MyThread mythread2 = new MyThread("Pong", pp); mythread1.thread.Join(); mythread2.thread.Join(); Console.WriteLine("\nThe Ball Stops Bouncing."); Console.Read(); } }
|
Thread Monitoring
To see specific threads for each running process, use Naveen K Kohli's Thread Monitoring C#.NET Application, shown below:
Within a C program, to list processes on a Linux machine use this system call takes no arguments but returns a 32-bit integer:
|
Thread Pooling
Thread pooling ensures stability during heavy load because it limits the maximum number of concurrent threads which are created to the number allocated to the thread pool. For best performance, use the CLR resident Thread Pool QueueUserWorkItem WaitCallback method of the CGSRMgrThread class _vecThread.Add(t); Checklist: ASP.NET Performance from the Microsoft MSDN library has this advice for Threading:
|
Related: