Csharp/C Sharp/Thread/Thread Sync

Материал из .Net Framework эксперт
Версия от 11:42, 26 мая 2010; Admin (обсуждение | вклад) (1 версия)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Another way to use lock to synchronize access to an object

/*
C#: The Complete Reference 
by Herbert Schildt 
Publisher: Osborne/McGraw-Hill (March 8, 2002)
ISBN: 0072134852
*/

// Another way to use lock to synchronize access to an object.  
 
using System; 
using System.Threading; 
 
class SumArray {  
  int sum;  
  
  public int sumIt(int[] nums) {  
    sum = 0; // reset sum  
  
    for(int i=0; i < nums.Length; i++) {  
      sum += nums[i];  
      Console.WriteLine("Running total for " +  
             Thread.CurrentThread.Name +  
             " is " + sum);  
      Thread.Sleep(10); // allow task-switch  
    }  
    return sum; 
  }  
}   
  
class MyThread {  
  public Thread thrd;  
  int[] a;  
  int answer; 
 
  /* Create one SumArray object for all 
     instances of MyThread. */ 
  static SumArray sa = new SumArray();  
 
  // Construct a new thread.  
  public MyThread(string name, int[] nums) {  
    a = nums;  
    thrd = new Thread(new ThreadStart(this.run)); 
    thrd.Name = name; 
    thrd.Start(); // start the thread  
  }  
  
  // Begin execution of new thread.  
  void run() {  
    Console.WriteLine(thrd.Name + " starting.");  
  
    // Lock calls to sumIt().  
    lock(sa) answer = sa.sumIt(a); 
 
    Console.WriteLine("Sum for " + thrd.Name +  
                       " is " + answer);  
  
    Console.WriteLine(thrd.Name + " terminating.");  
  }  
}  
  
public class Sync2 {  
  public static void Main() {  
    int[] a = {1, 2, 3, 4, 5};  
  
    MyThread mt1 = new MyThread("Child #1", a);  
    MyThread mt2 = new MyThread("Child #2", a);  
  
    mt1.thrd.Join();  
    mt2.thrd.Join();  
  }  
}


A synchronized shared buffer implementation

using System;
using System.Threading;
public class SynchronizedBuffer
{
   private int buffer = -1; 
   private int occupiedBufferCount = 0;  
   public int Buffer
   {      
      get
      { 
         Monitor.Enter( this );
         if ( occupiedBufferCount == 0 )
         {
            Console.WriteLine(Thread.CurrentThread.Name + " tries to read." );
            DisplayState( "Buffer empty. " +Thread.CurrentThread.Name + " waits." );
            Monitor.Wait( this );
         } 
         --occupiedBufferCount;    
                              
         DisplayState( Thread.CurrentThread.Name + " reads " + buffer );
         Monitor.Pulse( this );
         int bufferCopy = buffer;
         Monitor.Exit( this );
         return bufferCopy;
      }
      set
      {
         Monitor.Enter( this );
         if ( occupiedBufferCount == 1 )
         {
            Console.WriteLine(Thread.CurrentThread.Name + " tries to write." );
            DisplayState( "Buffer full. " + Thread.CurrentThread.Name + " waits." );
            Monitor.Wait( this );
         }
         buffer = value;
         ++occupiedBufferCount;
         DisplayState( Thread.CurrentThread.Name + " writes " + buffer );
         Monitor.Pulse( this );
         Monitor.Exit( this );
      } 
   }
   public void DisplayState( string operation )
   {
      Console.WriteLine( "{0,-35}{1,-9}{2}\n",operation, buffer, occupiedBufferCount );
   }
   static void Main( string[] args )
   {
      SynchronizedBuffer shared = new SynchronizedBuffer();
      Random random = new Random();
      Console.WriteLine( "{0,-35}{1,-9}{2}\n","Operation", "Buffer", "Occupied Count" );
      shared.DisplayState( "Initial state" );
      Producer producer = new Producer( shared, random );
      Consumer consumer = new Consumer( shared, random );
      Thread producerThread = new Thread( new ThreadStart( producer.Produce ) );
      producerThread.Name = "Producer";
      Thread consumerThread = new Thread( new ThreadStart( consumer.Consume ) );
      consumerThread.Name = "Consumer";
      producerThread.Start();
      consumerThread.Start();
   }
}
public class Consumer
{
   private SynchronizedBuffer sharedLocation;
   private Random randomSleepTime;
   public Consumer( SynchronizedBuffer shared, Random random )
   {
      sharedLocation = shared;
      randomSleepTime = random;
   }
   public void Consume()
   {
      int sum = 0;
      for ( int count = 1; count <= 10; count++ )
      {
         Thread.Sleep( randomSleepTime.Next( 1, 1001 ) );
         sum += sharedLocation.Buffer;
      }
      Console.WriteLine("{0} read values totaling: {1}.\nTerminating {0}.",Thread.CurrentThread.Name, sum );
   }
}
public class Producer 
{
   private SynchronizedBuffer sharedLocation;
   private Random randomSleepTime;
   public Producer( SynchronizedBuffer shared, Random random )
   {
      sharedLocation = shared;
      randomSleepTime = random;
   }
   public void Produce()
   {
      for ( int count = 1; count <= 10; count++ ) 
      {
         Thread.Sleep( randomSleepTime.Next( 1, 1001 ) );
         sharedLocation.Buffer = count; 
      }
      Console.WriteLine( "{0} done producing.\nTerminating {0}.",Thread.CurrentThread.Name );
   }
}


Asynchronous Calls:A Simple Example 1

/*
A Programmer"s Introduction to C# (Second Edition)
by Eric Gunnerson
Publisher: Apress  L.P.
ISBN: 1-893115-62-3
*/
// 29 - Threading and Asynchronous Operations\Asynchronous Calls\A Simple Example
// copyright 2000 Eric Gunnerson
using System;

public class AsynchronousCallsASimpleExample
{
    public static void Main()
    {
        AsyncCaller ac = new AsyncCaller();
        ac.CallWriteLine("Hello");
    }
}
public class AsyncCaller
{
    // Declare a delegate that will match Console.WriteLine("string");
    delegate void FuncToCall(string s);
    
    public void CallWriteLine(string s)
    {
        // delegate points to function to call
        // start the async call
        // wait for completion
        FuncToCall func = new FuncToCall(Console.WriteLine);
        IAsyncResult iar = func.BeginInvoke(s, null, null);
        func.EndInvoke(iar);
    }
}


Asynchronous Calls:A Simple Example 2

/*
A Programmer"s Introduction to C# (Second Edition)
by Eric Gunnerson
Publisher: Apress  L.P.
ISBN: 1-893115-62-3
*/
// 29 - Threading and Asynchronous Operations\Asynchronous Calls\A Simple Example
// copyright 2000 Eric Gunnerson
using System;
public class AsynchronousCallsASimpleExample2
{
    public static void Main()
    {
        AsyncCaller ac = new AsyncCaller();
        
        ac.CallWriteLineWithCallback("Hello There");
        
        System.Threading.Thread.Sleep(1000);
    }
}
public class AsyncCaller
{
    // Declare a delegate that will match Console.WriteLine("string");
    delegate void FuncToCall(string s);
    
    public void WriteLineCallback(IAsyncResult iar)
    {
        Console.WriteLine("In WriteLineCallback");
        FuncToCall func = (FuncToCall) iar.AsyncState;
        func.EndInvoke(iar);
    }
    
    public void CallWriteLineWithCallback(string s)
    {
        FuncToCall func = new FuncToCall(Console.WriteLine);
        func.BeginInvoke(s, 
        new AsyncCallback(WriteLineCallback), 
        func); // shows up as iar.AsyncState in callback
    }
}


Asynchronous Calls:Return Values

/*
A Programmer"s Introduction to C# (Second Edition)
by Eric Gunnerson
Publisher: Apress  L.P.
ISBN: 1-893115-62-3
*/
// 29 - Threading and Asynchronous Operations\Asynchronous Calls\Return Values
// copyright 2000 Eric Gunnerson
using System;
using System.Threading;

public class AsynchronousCallsReturnValues
{
    public static void Main()
    {
        AsyncCaller ac = new AsyncCaller();
        
        ac.CallMathCallback(new AsyncCaller.MathFunctionToCall(Math.Sin), 0.0, 1.0, 0.2);
        Thread.Sleep(2000);
    }
}
public class AsyncCaller
{
    public delegate double MathFunctionToCall(double arg);
    
    public void MathCallback(IAsyncResult iar)
    {
        MathFunctionToCall mc = (MathFunctionToCall) iar.AsyncState;
        double result = mc.EndInvoke(iar);
        Console.WriteLine("Function value = {0}", result);
    }
    public void CallMathCallback(MathFunctionToCall mathFunc,
    double start,
    double end,
    double increment)
    {
        AsyncCallback cb = new AsyncCallback(MathCallback);
        
        while (start < end)
        {    
            Console.WriteLine("BeginInvoke: {0}", start);
            mathFunc.BeginInvoke(start, cb, mathFunc);
            start += increment;
        }
    }
}


Asynchronous Calls:Waiting for Completion

/*
A Programmer"s Introduction to C# (Second Edition)
by Eric Gunnerson
Publisher: Apress  L.P.
ISBN: 1-893115-62-3
*/
// 29 - Threading and Asynchronous Operations\Asynchronous Calls\Waiting for Completion
// copyright 2000 Eric Gunnerson
using System;
using System.Threading;

public class AsynchronousCallsWaitingforCompletion1
{
    public static double DoCalculation(double value)
    {
        Console.WriteLine("DoCalculation: {0}", value);
        Thread.Sleep(250);
        return(Math.Cos(value));
    }
    
    public static void Main()
    {
        AsyncCaller ac = new AsyncCaller();
        
        ac.CallMathCallback(new AsyncCaller.MathFunctionToCall(DoCalculation));
        //Thread.Sleep(500);        // no longer needed
    }
}
public class AsyncCaller
{
    public delegate double MathFunctionToCall(double arg);
    
    public void MathCallback(IAsyncResult iar)
    {
        MathFunctionToCall mc = (MathFunctionToCall) iar.AsyncState;
        double result = mc.EndInvoke(iar);
        Console.WriteLine("Function value = {0}", result);
    }
    
    WaitHandle DoInvoke(MathFunctionToCall mathFunc, double value)
    {
        AsyncCallback cb = new AsyncCallback(MathCallback);
        
        IAsyncResult asyncResult = 
        mathFunc.BeginInvoke(value, cb, mathFunc);
        return(asyncResult.AsyncWaitHandle);
    }
    
    public void CallMathCallback(MathFunctionToCall mathFunc)
    {
        WaitHandle[] waitArray = new WaitHandle[4];
        
        Console.WriteLine("Begin Invoke");
        waitArray[0] = DoInvoke(mathFunc, 0.1);
        waitArray[1] = DoInvoke(mathFunc, 0.5);
        waitArray[2] = DoInvoke(mathFunc, 1.0);
        waitArray[3] = DoInvoke(mathFunc, 3.14159);
        Console.WriteLine("Begin Invoke Done");
        
        Console.WriteLine("Waiting for completion");
        WaitHandle.WaitAll(waitArray, 10000, false);
        Console.WriteLine("Completion achieved");
    }
}


Asynchronous Calls:Waiting for Completion 2

/*
A Programmer"s Introduction to C# (Second Edition)
by Eric Gunnerson
Publisher: Apress  L.P.
ISBN: 1-893115-62-3
*/
// 29 - Threading and Asynchronous Operations\Asynchronous Calls\Waiting for Completion
// copyright 2000 Eric Gunnerson
using System;
using System.Threading;

public class AsynchronousCallsWaitingforCompletion2
{
    public static double DoCalculation(double value)
    {
        Console.WriteLine("DoCalculation: {0}", value);
        Thread.Sleep(250);
        return(Math.Cos(value));
    }
    
    public static void Main()
    {
        AsyncCaller ac = new AsyncCaller();
        
        ac.CallMathCallback(new AsyncCaller.MathFunctionToCall(DoCalculation));
    }
}
public class AsyncCallTracker
{
    Delegate function;
    AutoResetEvent doneEvent;
    
    public AutoResetEvent DoneEvent
    {
        get
        {
            return(doneEvent);
        }
    }
    
    public Delegate Function
    {
        get
        {
            return(function);
        }
    }
    
    public AsyncCallTracker(Delegate function)
    {
        this.function = function;
        doneEvent = new AutoResetEvent(false);
    }
}
public class AsyncCaller
{
    public delegate double MathFunctionToCall(double arg);
    
    public void MathCallback(IAsyncResult iar)
    {
        AsyncCallTracker callTracker = (AsyncCallTracker) iar.AsyncState;
        MathFunctionToCall func = (MathFunctionToCall) callTracker.Function;
        double result = func.EndInvoke(iar);
        Console.WriteLine("Function value = {0}", result);
        callTracker.DoneEvent.Set();
    }
    
    WaitHandle DoInvoke(MathFunctionToCall mathFunc, double value)
    {
        AsyncCallTracker callTracker = new AsyncCallTracker(mathFunc);
        
        AsyncCallback cb = new AsyncCallback(MathCallback);
        IAsyncResult asyncResult = mathFunc.BeginInvoke(value, cb, callTracker);
        return(callTracker.DoneEvent);
    }
    
    public void CallMathCallback(MathFunctionToCall mathFunc)
    {
        WaitHandle[] waitArray = new WaitHandle[4];
        
        Console.WriteLine("Begin Invoke");
        waitArray[0] = DoInvoke(mathFunc, 0.1);
        waitArray[1] = DoInvoke(mathFunc, 0.5);
        waitArray[2] = DoInvoke(mathFunc, 1.0);
        waitArray[3] = DoInvoke(mathFunc, 3.14159);
        Console.WriteLine("Begin Invoke Done");
        
        Console.WriteLine("Waiting for completion");
        WaitHandle.WaitAll(waitArray, 10000, false);
        Console.WriteLine("Completion achieved");
    }
}


Data Protection and Synchronization:A Slightly Broken Example

/*
A Programmer"s Introduction to C# (Second Edition)
by Eric Gunnerson
Publisher: Apress  L.P.
ISBN: 1-893115-62-3
*/
// 29 - Threading and Asynchronous Operations\Data Protection and Synchronization\A Slightly Broken Example
// copyright 2000 Eric Gunnerson
using System;
using System.Threading;
class Val
{
    int number = 1;
    
    public void Bump()
    {
        int temp = number;
        number = temp + 2;
    }
    
    public override string ToString()
    {
        return(number.ToString());
    }
    
    public void DoBump()
    {
        for (int i = 0; i < 5; i++)
        {
            Bump();
            Console.WriteLine("number = {0}", number);
        }
    }
}
public class DataProtectionandSynchronizationASlightlyBrokenExample
{
    public static void Main()
    {
        Val v = new Val();
        
        for (int threadNum = 0; threadNum < 5; threadNum++)
        {
            Thread thread = new Thread(new ThreadStart(v.DoBump));
            thread.Start();
        }
    }
}


illustrates the use of the Mutex object

/*
Mastering Visual C# .NET
by Jason Price, Mike Gunderloy
Publisher: Sybex;
ISBN: 0782129110
*/
/*
  Example14_11.cs illustrates the use of the Mutex object
*/
using System;
using System.Threading;
public class Example14_11 
{
  // a shared counter
  private static int Runs = 0;
  // a mutex
  static Mutex mtx;
  // the CountUp method increments the shared counter
  public static void CountUp() 
  {
    while (Runs < 10)
    {
      // acquire the mutex
      mtx.WaitOne();
      int Temp = Runs;
      Temp++;
      Console.WriteLine(Thread.CurrentThread.Name + " " + Temp);
      Thread.Sleep(1000);
      Runs = Temp;
      // release the mutex
      mtx.ReleaseMutex();
    } 
  }
  public static void Main() 
  {
    // create the mutex
    mtx = new Mutex(false, "RunsMutex");
    // create and launch two threads
    Thread t2 = new Thread(new ThreadStart(CountUp));
    t2.Name = "t2";
    Thread t3 = new Thread(new ThreadStart(CountUp));
    t3.Name = "t3";
    t2.Start();
    t3.Start();
  }
}


My Main Class Async Call back

/*
 * C# Programmers Pocket Consultant
 * Author: Gregory S. MacBeth
 * Email: gmacbeth@comporium.net
 * Create Date: June 27, 2003
 * Last Modified Date:
 * Version: 1
 */
using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;
namespace Client.Chapter_15___Threading
{
  public class MyMainClassAsyncCallback
  {
    delegate int MyDelegate(string s, ref int a, ref int b);
    static void Main(string[] args)
    {
      MyDelegate X = new MyDelegate(DoSomething);
      int a = 0;
      int b = 0;
      //Making Async Call that calls a callback when finished
      AsyncCallback cb = new AsyncCallback(DoSomething2);
      IAsyncResult ar = X.BeginInvoke("Hello", ref a, ref b, cb, null);
      Console.ReadLine();
    }
    //My Async Method
    static int DoSomething(string s, ref int a, ref int b)
    {
      a = 10;
      b = 100;
      Console.WriteLine("Fired! DoSomething1");
      return 0;
    }
    //Mycallback method when finished running DoSomehting
    static void DoSomething2(IAsyncResult ar)
    {
      int a = 0;
      int b = 0;
      Console.WriteLine("Fired! DoSomething2");
      //Get the delegate
      MyDelegate X = (MyDelegate)((AsyncResult)ar).AsyncDelegate;
      //get results
      X.EndInvoke(ref a, ref b, ar);
      Console.WriteLine(a);
      Console.WriteLine(b);
    }
  }
}


MyMain Class Async Wait Timeout

/*
 * C# Programmers Pocket Consultant
 * Author: Gregory S. MacBeth
 * Email: gmacbeth@comporium.net
 * Create Date: June 27, 2003
 * Last Modified Date:
 * Version: 1
 */
using System;
using System.Runtime.Remoting.Messaging;
namespace Client.Chapter_15___Threading
{
  public class MyMainClassAsyncWaitTimeout
  {
    delegate int MyDelegate(string s, ref int a, ref int b);
    static void Main(string[] args)
    {
      MyDelegate X = new MyDelegate(DoSomething);
      int a = 0;
      int b = 0;
      IAsyncResult ar = X.BeginInvoke("Hello", ref a, ref b, null, null);
      ar.AsyncWaitHandle.WaitOne(10000, false);
      if (ar.IsCompleted)
      {
        int c = 0;
        int d = 0;
        //get results
        X.EndInvoke(ref c, ref d, ar);
        Console.WriteLine(c);
        Console.WriteLine(d);
      }
    }
    //My Async Method
    static int DoSomething(string s, ref int a, ref int b)
    {
      a = 10;
      b = 100;
      Console.WriteLine("Fired! DoSomething1");
      return 0;
    }
  }
}


Threading and Asynchronous Operations:Access Reordering and Volatile

/*
A Programmer"s Introduction to C# (Second Edition)
by Eric Gunnerson
Publisher: Apress  L.P.
ISBN: 1-893115-62-3
*/
// 29 - Threading and Asynchronous Operations\Access Reordering and Volatile
// copyright 2000 Eric Gunnerson
using System;
using System.Threading;
class Problem
{
    int x;
    int y;
    int curx;
    int cury;
    
    public Problem()
    {
        x = 0;
        y = 0;
    }
    
    public void Process1()
    {
        x = 1;
        cury = y;
    }
    
    public void Process2()
    {
        y = 1;
        curx = x;
    }
    
    public void TestCurrent()
    {
        Console.WriteLine("curx, cury: {0} {1}", curx, cury);
    }
}
public class AccessReorderingandVolatile
{
    public static void Main()
    {
        Problem p = new Problem();
        
        Thread t1 = new Thread(new ThreadStart(p.Process1));        
        Thread t2 = new Thread(new ThreadStart(p.Process2));        
        t1.Start();
        t2.Start();
        
        t1.Join();
        t2.Join();
        
        p.TestCurrent();
    }
}


Threading Class Mutex

/*
 * C# Programmers Pocket Consultant
 * Author: Gregory S. MacBeth
 * Email: gmacbeth@comporium.net
 * Create Date: June 27, 2003
 * Last Modified Date:
 * Version: 1
 */
using System;
using System.Collections;
using System.Threading;
namespace Client.Chapter_15___Threading
{
  public class ThreadingClassMutex
  {
    public static Thread ThreadOne = new Thread(new ThreadStart(MutexExample));
    public static ArrayList MyList = new ArrayList();
    private static Mutex MyMutex = new Mutex(false, "MyMutex");
    public ThreadingClassMutex()
    {
      MyList.Add("Test1");
      MyList.Add("Test2");
    }
    static void Main(string[] args)
    {
      ThreadOne.Start();
    }
    protected static void MutexExample()
    {
      MyMutex.WaitOne();
      MyList.Add("Test3");
      MyMutex.ReleaseMutex();
    }
  }
}


Use lock to synchronize access to an object

/*
C#: The Complete Reference 
by Herbert Schildt 
Publisher: Osborne/McGraw-Hill (March 8, 2002)
ISBN: 0072134852
*/

// Use lock to synchronize access to an object.  
 
using System; 
using System.Threading; 
 
class SumArray {  
  int sum;  
  
  public int sumIt(int[] nums) {  
    lock(this) { // lock the entire method 
      sum = 0; // reset sum  
    
      for(int i=0; i < nums.Length; i++) {  
        sum += nums[i];  
        Console.WriteLine("Running total for " +  
               Thread.CurrentThread.Name +  
               " is " + sum);  
        Thread.Sleep(10); // allow task-switch  
      }  
      return sum; 
    } 
  }  
}   
  
class MyThread {  
  public Thread thrd;  
  int[] a;  
  int answer; 
 
  /* Create one SumArray object for all 
     instances of MyThread. */ 
  static SumArray sa = new SumArray();  
 
  // Construct a new thread.  
  public MyThread(string name, int[] nums) {  
    a = nums;  
    thrd = new Thread(new ThreadStart(this.run)); 
    thrd.Name = name; 
    thrd.Start(); // start the thread  
  }  
  
  // Begin execution of new thread.  
  void run() {  
    Console.WriteLine(thrd.Name + " starting.");  
  
    answer = sa.sumIt(a);           
 
    Console.WriteLine("Sum for " + thrd.Name +  
                       " is " + answer);  
  
    Console.WriteLine(thrd.Name + " terminating.");  
  }  
}  
  
public class Sync {  
  public static void Main() {  
    int[] a = {1, 2, 3, 4, 5};  
  
    MyThread mt1 = new MyThread("Child #1", a);  
    MyThread mt2 = new MyThread("Child #2", a);  
  
    mt1.thrd.Join();  
    mt2.thrd.Join();  
  }  
}


Use MethodImplAttribute to synchronize a method

/*
C#: The Complete Reference 
by Herbert Schildt 
Publisher: Osborne/McGraw-Hill (March 8, 2002)
ISBN: 0072134852
*/

// Use MethodImplAttribute to synchronize a method. 
 
using System; 
using System.Threading; 
using System.Runtime.rupilerServices; 
 
class TickTock { 
 
  /* The following attribute synchronizes the entire 
     tick() method. */ 
  [MethodImplAttribute(MethodImplOptions.Synchronized)] 
  public void tick(bool running) { 
    if(!running) { // stop the clock 
      Monitor.Pulse(this); // notify any waiting threads 
      return; 
    } 
 
    Console.Write("Tick "); 
    Monitor.Pulse(this); // let tock() run 
   
    Monitor.Wait(this); // wait for tock() to complete 
  } 
 
 
  /* The following attribute synchronizes the entire 
     tock() method. */ 
  [MethodImplAttribute(MethodImplOptions.Synchronized)] 
  public void tock(bool running) { 
    if(!running) { // stop the clock 
      Monitor.Pulse(this); // notify any waiting threads 
      return; 
    } 
 
    Console.WriteLine("Tock"); 
    Monitor.Pulse(this); // let tick() run 
 
    Monitor.Wait(this); // wait for tick to complete 
  } 
 
}  
 
class MyThread { 
  public Thread thrd; 
  TickTock ttOb; 
 
  // Construct a new thread. 
  public MyThread(string name, TickTock tt) { 
    thrd = new Thread(new ThreadStart(this.run)); 
    ttOb = tt; 
    thrd.Name = name; 
    thrd.Start();  
  } 
 
  // Begin execution of new thread. 
  void run() { 
    if(thrd.Name == "Tick") { 
      for(int i=0; i<5; i++) ttOb.tick(true); 
      ttOb.tick(false); 
    } 
    else { 
      for(int i=0; i<5; i++) ttOb.tock(true); 
      ttOb.tock(false); 
    } 
  } 
} 
 
public class TickingClock1 { 
  public static void Main() { 
    TickTock tt = new TickTock(); 
    MyThread mt1 = new MyThread("Tick", tt); 
    MyThread mt2 = new MyThread("Tock", tt); 
 
    mt1.thrd.Join(); 
    mt2.thrd.Join(); 
    Console.WriteLine("Clock Stopped"); 
  } 
}


Use Wait() and Pulse() to create a ticking clock

/*
C#: The Complete Reference 
by Herbert Schildt 
Publisher: Osborne/McGraw-Hill (March 8, 2002)
ISBN: 0072134852
*/

// Use Wait() and Pulse() to create a ticking clock. 
 
using System; 
using System.Threading; 
 
class TickTock { 
 
  public void tick(bool running) { 
    lock(this) { 
      if(!running) { // stop the clock 
        Monitor.Pulse(this); // notify any waiting threads 
        return; 
      } 
 
      Console.Write("Tick "); 
      Monitor.Pulse(this); // let tock() run 
   
      Monitor.Wait(this); // wait for tock() to complete 
    } 
  } 
 
  public void tock(bool running) { 
    lock(this) { 
      if(!running) { // stop the clock 
        Monitor.Pulse(this); // notify any waiting threads 
        return; 
      } 
 
      Console.WriteLine("Tock"); 
      Monitor.Pulse(this); // let tick() run 
 
      Monitor.Wait(this); // wait for tick to complete 
    } 
  } 
}  
 
class MyThread { 
  public Thread thrd; 
  TickTock ttOb; 
 
  // Construct a new thread. 
  public MyThread(string name, TickTock tt) { 
    thrd = new Thread(new ThreadStart(this.run)); 
    ttOb = tt; 
    thrd.Name = name; 
    thrd.Start();  
  } 
 
  // Begin execution of new thread. 
  void run() { 
    if(thrd.Name == "Tick") { 
      for(int i=0; i<5; i++) ttOb.tick(true); 
      ttOb.tick(false); 
    } 
    else { 
      for(int i=0; i<5; i++) ttOb.tock(true); 
      ttOb.tock(false); 
    } 
  } 
} 
 
public class TickingClock { 
  public static void Main() { 
    TickTock tt = new TickTock(); 
    MyThread mt1 = new MyThread("Tick", tt); 
    MyThread mt2 = new MyThread("Tock", tt); 
 
    mt1.thrd.Join(); 
    mt2.thrd.Join(); 
    Console.WriteLine("Clock Stopped"); 
  } 
}