Csharp/C Sharp/Thread/Thread Sync — различия между версиями
Admin (обсуждение | вклад) м (1 версия) |
|
(нет различий)
|
Версия 15:31, 26 мая 2010
Содержание
- 1 Another way to use lock to synchronize access to an object
- 2 A synchronized shared buffer implementation
- 3 Asynchronous Calls:A Simple Example 1
- 4 Asynchronous Calls:A Simple Example 2
- 5 Asynchronous Calls:Return Values
- 6 Asynchronous Calls:Waiting for Completion
- 7 Asynchronous Calls:Waiting for Completion 2
- 8 Data Protection and Synchronization:A Slightly Broken Example
- 9 illustrates the use of the Mutex object
- 10 My Main Class Async Call back
- 11 MyMain Class Async Wait Timeout
- 12 Threading and Asynchronous Operations:Access Reordering and Volatile
- 13 Threading Class Mutex
- 14 Use lock to synchronize access to an object
- 15 Use MethodImplAttribute to synchronize a method
- 16 Use Wait() and Pulse() to create a ticking clock
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();
}
}
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");
}
}