+
< Prev  1 2 3 4 5  Next >

C# Threading

  1. Simple multithreaded example in c#.

    The method passed as the ThreadStart delegate can have no arguments and must return void.

    using System;
    using System.Threading;
    
    public class ThreadTest {
    	public class MyJob {
    		public int repetitions=1;
    		public void runme() {
    			for(int i=0;i<repetitions;i++) {
    				Thread.Sleep(1000);
    			   Console.WriteLine(Thread.CurrentThread.Name);
    			}
    		}
    	}
    	
        public static void Main(string[] args) {
    	MyJob myJob = new MyJob();
    	myJob.repetitions=3;
    	Thread thread = new Thread(new ThreadStart(myJob.runme));
    	thread.Name = "first";
            thread.Start();
    	
    	MyJob myJob2 = new MyJob();
    	myJob2.repetitions=5;
    	Thread thread2 = new Thread(new ThreadStart(myJob2.runme));
    	thread2.Name = "second";
            thread2.Start();
        }
    }
    

    The output will not necessarily be the same; this time it produces:

    first
    second
    first
    second
    first
    second
    second
    second
    
  2. Concurrency

    When two threads change the same data, Bad Things(tm) can happen. In the following example two threads increment and then decrement a global variable. In C# incrementing and decrementing are not necessarily atomic.

    using System;
    using System.Threading;
    
    public class ThreadTest2 {
    public class MyJob {
    	public static int counter = 0;
    	public int repetitions=100000000;
    	public void runme() {
    		for(int i=0;i<repetitions;i++) {
    			counter++;
    			counter--;
    		}
    	Console.WriteLine(Thread.CurrentThread.Name+": "+counter);
    	}
    }
    	
        public static void Main(string[] args) {
    	MyJob myJob = new MyJob();
    	Thread thread = new Thread(new ThreadStart(myJob.runme));
    	thread.Name = "first";
            
    	MyJob myJob2 = new MyJob();
    	Thread thread2 = new Thread(new ThreadStart(myJob2.runme));
    	thread2.Name = "second";
            
    	thread.Start();
    	thread2.Start();
        }
    }
    

    The results can be:

                first: 0
                second: 0
    

    or sometimes

                first: 1
                second: 0
    

    We can never be sure of the outcome. We can overcome this in several ways.

  3. Using Threading.Interlocked methods.

    Using Threading.Interlocked.Increment the CLR will guarentee an atomic operation. Another interesting method in the namespace is "Exchange()" which swaps two values atomically.

    public class MyJob {
    	public static int counter = 0;
    	public int repetitions=100000000;
    	public void runme() {
    		for(int i=0;i<repetitions;i++) {
    			System.Threading.Interlocked.Increment(ref i);
    			System.Threading.Interlocked.Decrement(ref i);
    		}
    		Console.WriteLine(Thread.CurrentThread.Name+": "+counter);
    		
    	}
    }
    

    The results will always be:

                first: 0
                second: 0
    
  4. Using "lock()" to synchronize

    lock() takes an object as the argument. In this example we can use "this" since both threads are using the same "MyJob" instance. In static functions you can use the "type" object, e.g., "lock(typeof(Util))" to synchronize.

    public class MyJob {
    public static int counter = 0;
    public int repetitions=100000000;
    public void runme() {
    	lock(this) {
    	   for(int i=0;i<repetitions;i++) {
    		counter++;
    		counter--;
    	   }
    	}
    	Console.WriteLine(Thread.CurrentThread.Name+": "+counter);
      }
    }
    
  5. The Synchronization attribute can be used.

    This locks the entire object - everything is single access. Note the object must descend from ContextBoundObject.

    [System.Runtime.Remoting.Contexts.Synchronization]
    public class MyJob: ContextBoundObject {
    	public static int counter = 0;
    	public int repetitions=100000000;
    	public void runme() {
    		for(int i=0;i<repetitions;i++) {
    			counter++;
    			counter--;
    		}
    		Console.WriteLine(Thread.CurrentThread.Name+": "+counter);
    		
    	}
    }
    
  6. Example of using Join()

    Join() hitches the fate of a thread to the current thread. Execution of the calling thread will wait until the callee's process completes. In this example we Join on a thread that takes 2 seconds to complete, then Join on a second that still has 2 seconds to go.

    using System;
    using System.Threading;
    //Example showing use of Join()
    public class ThreadTest4 {
    	public class MyJob {
    		public static int counter = 0;
    		public int waitfor=1;
    		public int finalState = -1;
    		//sleeps then fills in finalState to simulate work done
    		public void runme() {
    		Thread.Sleep(waitfor);
    		finalState = waitfor;
    		Console.WriteLine(Thread.CurrentThread.Name+" finished sleeping finalState: "+finalState);
    		}
    	}
    	
        public static void Main(string[] args) {
    	MyJob myJob = new MyJob();
    	myJob.waitfor = 2000;
    	Thread thread = new Thread(new ThreadStart(myJob.runme));
    	thread.Name = "first";
            
    	MyJob myJob2 = new MyJob();
    	myJob2.waitfor = 4000;
    	Thread thread2 = new Thread(new ThreadStart(myJob2.runme));
    	thread2.Name = "second";
            
    	thread.Start();
    	thread2.Start();
    	
    	Console.WriteLine("After start.");
    	Console.WriteLine("Before first join.");
    	thread.Join();
    	Console.WriteLine("After first join.");
    	Console.WriteLine("Before second join.");
    	thread2.Join();
    	Console.WriteLine("After second join.");
    	
    	Console.WriteLine("myJob.finalState="+myJob.finalState);
    	Console.WriteLine("myJob2.finalState="+myJob2.finalState);
        }
    }
    

    This produces

    After start.
    Before first join.
    first finished sleeping finalState: 2000
    After first join.
    Before second join.
    second finished sleeping finalState: 4000
    After second join.
    myJob.finalState=2000
    myJob2.finalState=4000
    
  7. Join() with timeout

    What if a process may never finish? The Join() method has an optional parameter specifying how many millisecs to wait. The Join will give up after that time and return a 'false'. The following example shows the main thread waiting 2 seconds for a job that will take 8 seconds. After waiting 2 seconds, it gets a 'false' value back implying it did not finish in 2 seconds. Lacking any mercy we waste the process and move on to wait for the second, which has already completed.

    using System;
    using System.Threading;
    //Example showing use of Join()
    public class ThreadTest6 {
    	public class MyJob {
    		public static int counter = 0;
    		public int waitfor=1;
    		public int finalState = -1;
    		//sleeps then fills in finalState to simulate work done
    		public void runme() {
    		Thread.Sleep(waitfor);
    		finalState = waitfor;
    		Console.WriteLine(Thread.CurrentThread.Name+" finished sleeping finalState: "+finalState);
    		}
    	}
    	
        public static void Main(string[] args) {
    	MyJob myJob = new MyJob();
    	myJob.waitfor = 8000;
    	Thread thread = new Thread(new ThreadStart(myJob.runme));
    	thread.Name = "first";
            
    	MyJob myJob2 = new MyJob();
    	myJob2.waitfor = 500;
    	Thread thread2 = new Thread(new ThreadStart(myJob2.runme));
    	thread2.Name = "second";
            
    	thread.Start();
    	thread2.Start();
    	
    	Console.WriteLine("After start.");
    	Console.WriteLine("Before first join.");
    	bool finished = thread.Join(2000);
    	if(!finished) { thread.Abort(); }
    	Console.WriteLine("finishedP:"+finished);
    	
    	Console.WriteLine("After first join.");
    	Console.WriteLine("Before second join.");
    	thread2.Join(2000);
    	Console.WriteLine("After second join.");
    	
    	Console.WriteLine("myJob.finalState="+myJob.finalState);
    	Console.WriteLine("myJob2.finalState="+myJob2.finalState);
        }
    }
    

    Which produces:

    After start.
    Before first join.
    second finished sleeping finalState: 500
    finishedP:False
    After first join.
    Before second join.
    After second join.
    myJob.finalState=-1
    myJob2.finalState=500
    
< Prev  1 2 3 4 5  Next >

+