Changing thread context in C# console application - c#

I have a C# console app in which I can get, among other things, input via a TCP socket connection. How do I switch to the main thread when I receive an input via the receive function over the socket?
So similar to something like this in WPF:
public void TaskDispatcher()
{
if (DispatcherObjectForTaskDispatcher.Thread != System.Threading.Thread.CurrentThread)
DispatcherObjectForTaskDispatcher.Invoke(new TaskDispatcherDelegate(TaskDispatcher));
else
{
// Do some thing in the UI thread
}
}

Just use a Producer-Consumer pattern as in the working example below. Enqueue jobs from other threads and let the main thread process the queued jobs from a job queue.
I used a timer thread and a user input thread to simulate 2 threads producing jobs. You could implement your TCP events to just enqueue a job in the job queue. You should store any relevant objects as arguments inside your job, for later processing. You must also define a function to be called by the job, which will run in the main thread.
The main thread is used here just for dequeueing jobs and processing them, but you could use any other thread for this purpose if you improve this code a little bit.
You could even implement multi-threading processing, on which more processing threads dequeue from the same job queue. Be aware this brings new concurrency problems which you may have to deal with. That's the drawback for gaining much more processing power in your application. Some scenarios are suitable for multi-threading processing (e.g. video / image processing) while some others are not.
The code below is a full working example written in a Visual Studio 2017, DotNET 4.6.1, console application project. Just copy, paste, and hit F5.
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading;
// Compiled and tested in: Visual Studio 2017, DotNET 4.6.1
namespace MyNamespace
{
public class Program
{
public static void Main(string[] args)
{
MyApplication app = new MyApplication();
app.Run();
}
}
public class MyApplication
{
private BlockingCollection<Job> JobQueue = new BlockingCollection<Job>();
private CancellationTokenSource JobCancellationTokenSource = new CancellationTokenSource();
private CancellationToken JobCancellationToken;
private Timer Timer;
private Thread UserInputThread;
public void Run()
{
// Give a name to the main thread:
Thread.CurrentThread.Name = "Main";
// Fires a Timer thread:
Timer = new Timer(new TimerCallback(TimerCallback), null, 1000, 2000);
// Fires a thread to read user inputs:
UserInputThread = new Thread(new ThreadStart(ReadUserInputs))
{
Name = "UserInputs",
IsBackground = true
};
UserInputThread.Start();
// Prepares a token to cancel the job queue:
JobCancellationToken = JobCancellationTokenSource.Token;
// Start processing jobs:
ProcessJobs();
// Clean up:
JobQueue.Dispose();
Timer.Dispose();
UserInputThread.Abort();
Console.WriteLine("Done.");
}
private void ProcessJobs()
{
try
{
// Checks if the blocking collection is still up for dequeueing:
while (!JobQueue.IsCompleted)
{
// The following line blocks the thread until a job is available or throws an exception in case the token is cancelled:
JobQueue.Take(JobCancellationToken).Run();
}
}
catch { }
}
private void ReadUserInputs()
{
// User input thread is running here.
ConsoleKey key = ConsoleKey.Enter;
// Reads user inputs and queue them for processing until the escape key is pressed:
while ((key = Console.ReadKey(true).Key) != ConsoleKey.Escape)
{
Job userInputJob = new Job("UserInput", this, new Action<ConsoleKey>(ProcessUserInputs), key);
JobQueue.Add(userInputJob);
}
// Stops processing the JobQueue:
JobCancellationTokenSource.Cancel();
}
private void ProcessUserInputs(ConsoleKey key)
{
// Main thread is running here.
Console.WriteLine($"You just typed '{key}'. (Thread: {Thread.CurrentThread.Name})");
}
private void TimerCallback(object param)
{
// Timer thread is running here.
Job job = new Job("TimerJob", this, new Action<string>(ProcessTimer), "A job from timer callback was processed.");
JobQueue.TryAdd(job); // Just enqueues the job for later processing
}
private void ProcessTimer(string message)
{
// Main thread is running here.
Console.WriteLine($"{message} (Thread: {Thread.CurrentThread.Name})");
}
}
/// <summary>
/// The Job class wraps an object's method call, with or without arguments. This method is called later, during the Job execution.
/// </summary>
public class Job
{
public string Name { get; }
private object TargetObject;
private Delegate TargetMethod;
private object[] Arguments;
public Job(string name, object obj, Delegate method, params object[] args)
{
Name = name;
TargetObject = obj;
TargetMethod = method;
Arguments = args;
}
public void Run()
{
try
{
TargetMethod.Method.Invoke(TargetObject, Arguments);
}
catch(Exception ex)
{
Debug.WriteLine($"Unexpected error running job '{Name}': {ex}");
}
}
}
}

Related

Forcing certain code to always run on the same thread

We have an old 3rd party system (let's call it Junksoft® 95) that we interface with via PowerShell (it exposes a COM object) and I'm in the process of wrapping it in a REST API (ASP.NET Framework 4.8 and WebAPI 2). I use the System.Management.Automation nuget package to create a PowerShell in which I instantiate Junksoft's COM API as a dynamic object that I then use:
//I'm omitting some exception handling and maintenance code for brevity
powerShell = System.Management.Automation.PowerShell.Create();
powerShell.AddScript("Add-Type -Path C:\Path\To\Junksoft\Scripting.dll");
powerShell.AddScript("New-Object Com.Junksoft.Scripting.ScriptingObject");
dynamic junksoftAPI = powerShell.Invoke()[0];
//Now we issue commands to junksoftAPI like this:
junksoftAPI.Login(user,pass);
int age = junksoftAPI.GetAgeByCustomerId(custId);
List<string> names = junksoftAPI.GetNames();
This works fine when I run all of this on the same thread (e.g. in a console application). However, for some reason this usually doesn't work when I put junksoftAPI into a System.Web.Caching.Cache and use it from different controllers in my web app. I say ususally because this actually works when ASP.NET happens to give the incoming call to the thread that junksoftAPI was created on. If it doesn't, Junksoft 95 gives me an error.
Is there any way for me to make sure that all interactions with junksoftAPI happen on the same thread?
Note that I don't want to turn the whole web application into a single-threaded application! The logic in the controllers and elswhere should happen like normal on different threads. It should only be the Junksoft interactions that happen on the Junksoft-specific thread, something like this:
[HttpGet]
public IHttpActionResult GetAge(...)
{
//finding customer ID in database...
...
int custAge = await Task.Run(() => {
//this should happen on the Junksoft-specific thread and not the next available thread
var cache = new System.Web.Caching.Cache();
var junksoftAPI = cache.Get(...); //This has previously been added to cache on the Junksoft-specific thread
return junksoftAPI.GetAgeByCustomerId(custId);
});
//prepare a response using custAge...
}
You can create your own singleton worker thread to achieve this. Here is the code which you can plug it into your web application.
public class JunkSoftRunner
{
private static JunkSoftRunner _instance;
//singleton pattern to restrict all the actions to be executed on a single thread only.
public static JunkSoftRunner Instance => _instance ?? (_instance = new JunkSoftRunner());
private readonly SemaphoreSlim _semaphore;
private readonly AutoResetEvent _newTaskRunSignal;
private TaskCompletionSource<object> _taskCompletionSource;
private Func<object> _func;
private JunkSoftRunner()
{
_semaphore = new SemaphoreSlim(1, 1);
_newTaskRunSignal = new AutoResetEvent(false);
var contextThread = new Thread(ThreadLooper)
{
Priority = ThreadPriority.Highest
};
contextThread.Start();
}
private void ThreadLooper()
{
while (true)
{
//wait till the next task signal is received.
_newTaskRunSignal.WaitOne();
//next task execution signal is received.
try
{
//try execute the task and get the result
var result = _func.Invoke();
//task executed successfully, set the result
_taskCompletionSource.SetResult(result);
}
catch (Exception ex)
{
//task execution threw an exception, set the exception and continue with the looper
_taskCompletionSource.SetException(ex);
}
}
}
public async Task<TResult> Run<TResult>(Func<TResult> func, CancellationToken cancellationToken = default(CancellationToken))
{
//allows only one thread to run at a time.
await _semaphore.WaitAsync(cancellationToken);
//thread has acquired the semaphore and entered
try
{
//create new task completion source to wait for func to get executed on the context thread
_taskCompletionSource = new TaskCompletionSource<object>();
//set the function to be executed by the context thread
_func = () => func();
//signal the waiting context thread that it is time to execute the task
_newTaskRunSignal.Set();
//wait and return the result till the task execution is finished on the context/looper thread.
return (TResult)await _taskCompletionSource.Task;
}
finally
{
//release the semaphore to allow other threads to acquire it.
_semaphore.Release();
}
}
}
Console Main Method for testing:
public class Program
{
//testing the junk soft runner
public static void Main()
{
//get the singleton instance
var softRunner = JunkSoftRunner.Instance;
//simulate web request on different threads
for (var i = 0; i < 10; i++)
{
var taskIndex = i;
//launch a web request on a new thread.
Task.Run(async () =>
{
Console.WriteLine($"Task{taskIndex} (ThreadID:'{Thread.CurrentThread.ManagedThreadId})' Launched");
return await softRunner.Run(() =>
{
Console.WriteLine($"->Task{taskIndex} Completed On '{Thread.CurrentThread.ManagedThreadId}' thread.");
return taskIndex;
});
});
}
}
}
Output:
Notice that, though the function was launched from the different threads, some portion of code got always executed always on the same context thread with ID: '5'.
But beware that, though all the web requests are executed on independent threads, they will eventually wait for some tasks to get executed on the singleton worker thread. This will eventually create a bottle neck in your web application. This is anyway your design limitation.
Here is how you could issue commands to the Junksoft API from a dedicated STA thread, using a BlockingCollection class:
public class JunksoftSTA : IDisposable
{
private readonly BlockingCollection<Action<Lazy<dynamic>>> _pump;
private readonly Thread _thread;
public JunksoftSTA()
{
_pump = new BlockingCollection<Action<Lazy<dynamic>>>();
_thread = new Thread(() =>
{
var lazyApi = new Lazy<dynamic>(() =>
{
var powerShell = System.Management.Automation.PowerShell.Create();
powerShell.AddScript("Add-Type -Path C:\Path\To\Junksoft.dll");
powerShell.AddScript("New-Object Com.Junksoft.ScriptingObject");
dynamic junksoftAPI = powerShell.Invoke()[0];
return junksoftAPI;
});
foreach (var action in _pump.GetConsumingEnumerable())
{
action(lazyApi);
}
});
_thread.SetApartmentState(ApartmentState.STA);
_thread.IsBackground = true;
_thread.Start();
}
public Task<T> CallAsync<T>(Func<dynamic, T> function)
{
var tcs = new TaskCompletionSource<T>(
TaskCreationOptions.RunContinuationsAsynchronously);
_pump.Add(lazyApi =>
{
try
{
var result = function(lazyApi.Value);
tcs.SetResult(result);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
});
return tcs.Task;
}
public Task CallAsync(Action<dynamic> action)
{
return CallAsync<object>(api => { action(api); return null; });
}
public void Dispose() => _pump.CompleteAdding();
public void Join() => _thread.Join();
}
The purpose of using the Lazy class is for surfacing a possible exception during the construction of the dynamic object, by propagating it to the callers.
...exceptions are cached. That is, if the factory method throws an exception the first time a thread tries to access the Value property of the Lazy<T> object, the same exception is thrown on every subsequent attempt.
Usage example:
// A static field stored somewhere
public static readonly JunksoftSTA JunksoftStatic = new JunksoftSTA();
await JunksoftStatic.CallAsync(api => { api.Login("x", "y"); });
int age = await JunksoftStatic.CallAsync(api => api.GetAgeByCustomerId(custId));
In case you find that a single STA thread is not enough to serve all the requests in a timely manner, you could add more STA threads, all of them running the same code (private readonly Thread[] _threads; etc). The BlockingCollection class is thread-safe and can be consumed concurrently by any number of threads.
If you did not say that was a 3rd party tool, I would have asumed it is a GUI class. For practical reasons, it is a very bad idea to have multiple threads write to them. .NET enforces a strict "only the creating thread shall write" rule, from 2.0 onward.
WebServers in general and ASP.Net in particular use a pretty big thread pool. We are talking 10's to 100's of Threads per Core. That means it is really hard to nail any request down to a specific Thread. You might as well not try.
Again, looking at the GUI classes might be your best bet. You could basically make a single thread with the sole purpose of immitating a GUI's Event Queue. The Main/UI Thread of your average Windows Forms application, is responsible for creating every GUI class instance. It is kept alive by polling/processing the event queue. It ends onlyx when it receies a cancel command, via teh Event Queue. Dispatching just puts orders into that Queue, so we can avoid Cross-Threading issues.

System.Threading.ThreadStateException (maybe a raise condition)

First, I would like to describe the general structure of the classes/methods involved in my problem.
I have a class which should start a thread cyclically. This thread deals with a function which writes the log entries into a log file. I realized this with a timer (System.Threading.Timer). Further there is a ThreadHandler class which keeps all threads in a list. These threads are controlled with the standard functions of System.Threading.Thread by the name of the thread. Now to the code that is affected by my problem:
In the constructor of my Log class (LogWriter) I call the method InitializeLoggerThread():
private void InitializeLoggerThread()
{
LoggerLoggingThread = new System.Threading.Thread(new System.Threading.ThreadStart(WriteLog));
LoggerLoggingThread.Name = LoggerLogginThreadName;
ObjectObserver.ThreadHandler.AddThread(LoggerLoggingThread); // Obersver class from which all objects can be accessed
}
The Timer itself will be starte das
public void StartLogging()
{
this.LoggerTimer = new System.Threading.Timer(LoggerCallback, null, 1000, LoggerInterval);
}
Furthermore the Log class contains the implementation oft he timer:
private const int LoggerInterval = 5000;
private System.Threading.Thread LoggerLoggingThread;
private static void LoggerCallback(object state)
{
if ((BufferCount > 0))
{
ObjectObserver.ThreadHandler.StartThread(LoggerLogginThreadName);
}
}
The ThreadHandler will strart the thread with te following function:
public void StartThread(string threadName)
{
lock (Locker)
{
if (GetThread(threadName).ThreadState == ThreadState.Stopped || GetThread(threadName).ThreadState == ThreadState.Unstarted)
{
GetThread(threadName).Start();
}
}
}
I have already checked the parameters etc.. Everything is correct in this case. Basically, when debugging, it seems that the threads all try to start the logger thread at the same time.
The thread will be calles by its name with the following function:
public Thread GetThread(string threadName)
{
foreach (Thread thread in Threads)
{
if (thread.Name == threadName)
{
return thread;
}
}
return null;
}
Now to my question: What is the error in my construct that I get
System.Threading.ThreadStateException at StartThread(...)
after the first execution a multiple attempt of the execution?
If desired, I can provide a copy&paste code of all important functions for debugging.
Thread passes through a set of states (taken from here):
Once thread is completed you cannot start it again. Your timer function tries to start Stopped thread though:
if (GetThread(threadName).ThreadState == ThreadState.Stopped ...
GetThread(threadName).Start();
There are awesome logging frameworks in .NET, such as NLog and log4net, please don't try to reinvent the wheel, most likely these frameworks already can do what you need and they are doing that much more efficiently.

Can a thread created in IIS notify about its timeout?

We have a long running process in web application. The process runs on a seperate thread created using this:
var arguments = new object[] { startDate, endDate, m_token };
ThreadPool.QueueUserWorkItem((WaitCallback)xyzmethod, arguments);
The thread is getting terminated due to limitation of IIS on how long an unattended thread can run. It gets terminated due to session timeout or application recycle.
I want to know if a thread can notify that it is about to timeout. Or send a notification just before getting terminated?
I have an helper class created that works at least for the WebDevServer, I assume it works for IIS as well. What this does is it Register a helper with the hostingenvironment which ends up calling Stop when the host is nicely taken down. Remember that in case of an uncontrolled process exit no threads will run anymore so this method is also not being called.
Usage
protected void Button1_Click(object sender, EventArgs e)
{
var hlp = Myhelper.Create();
var arguments = new object[] { "foo", 42 };
ThreadPool.QueueUserWorkItem(hlp.FooBar, arguments);
}
Helper
public class Myhelper:IRegisteredObject
{
private Myhelper(){}
// factory
public static Myhelper Create()
{
var hlp = new Myhelper();
HostingEnvironment.RegisterObject(hlp); // register ourself!
return hlp;
}
bool keepRunning = true;
ManualResetEvent mre = new ManualResetEvent(false);
ManualResetEvent stopped = new ManualResetEvent(false);
// our DoWork method!
public void FooBar(object args)
{
EventLog.WriteEntry(".NET Runtime", "started");
// implement your long running function,
// be sure to check regularly if you need to stop processing
while (keepRunning)
{
Trace.Write("+");
mre.WaitOne(1000); // do work, (used this instead of Thread.Sleep())
}
EventLog.WriteEntry(".NET Runtime","stopped");
HostingEnvironment.UnregisterObject(this);
stopped.Set(); // we are done!
}
// this gets caled when the HostingEnvironment is stopping
public void Stop(bool immediate)
{
keepRunning = false;
mre.Set(); // signal nicely
stopped.WaitOne(200); // wait max 200 ms before returning to prevent getting stuck
}
}

Replacing Thread.Suspend and Thread.Resume in a windows service

We have a service that does the following basic workflow:
1) Starts, reads config settings and performs some calculations in a large loop.
2) Each iteration of the loop, it needs to be able to check if the service has been told to stop. It performs database fetches, calculations then stores results. I am not confident on how well the code is done wrt SQL transactions so at this stage, happy to assume we are only checking for service stop at the start of each iteration.
3) After performing all iterations, the service "sleeps" for a period of time. Could be 5 minutes. Could be 12 hours. It needs to be able to "stop" in this sleep period!
Currently this is performed by the following:
private int threadSleepMinutes = 60;
private readonly Mutex mutTerminateService = new Mutex(false);
private Thread serviceThread;
private Thread serviceStopThread;
// Use this flag to allow the Start op to wait for serviceStopThread
// to get going before continuing to create the main loop thread
private volatile bool stopService = true;
public void Start()
{
this.serviceStopThread = new Thread(this.RunServiceStopThread);
this.serviceStopThread.IsBackground = true;
this.serviceStopThread.Start();
while (stopService)
{
Thread.Sleep(100);
}
// Some things renamed to anonymise... you get the idea!
this.serviceThread = new Thread(this.BigLoopMethod);
this.serviceThread.IsBackground = true;
this.serviceThread.Start();
}
public void Stop()
{
// Release the mutex to terminate the service
serviceStopThread.Resume();
// Wait 5s max
int timeout = 5000;
while (this.serviceThread.IsAlive && timeout > 0)
{
Thread.Sleep(100);
timeout -= 100;
}
}
private void RunServiceStopThread()
{
// To guarantee the same thread takes the mutex
// and releases it in dot net 4, do both ops in this single thread!
// Dot net 4 the Start() and Stop() are now usually on different threads.
mutTerminateService.WaitOne();
stopService = false;
// Suspend ourself
serviceStopThread.Suspend();
// Release the mutex
mutTerminateService.ReleaseMutex();
}
public void BigLoopMethod()
{
try
{
do
{
bool moreOperationsToGo = true; // Just dummy flags and 'stuff' methods here
while (moreOperationsToGo && !mutTerminateService.WaitOne(0))
{
DoStuff();
}
// Using this mutex here to sleep nicely - event driven.
// Gracefully continues after timeout and gracefully exits if
// triggered by the mutex.
}
while (!mutTerminateService.WaitOne(this.threadSleepMinutes * 60000));
}
catch (Exception ex)
{
// Exception handling & logging here
}
}
Now I get messages saying Suspend and Resume are deprecated. In my situation, I know exactly what code the suspend was run on since the call itself is what suspended it! Resume, I know exactly what it is going to do. The only reason this was even done in the first place was because the mutex worked fine in Start() and Stop() in dot net 3.5 but dot net 4.0 changed so that Start() and Stop() were in different threads AND they marked the workaround as obsolete!
Is there a nice way, non-obsolete way of doing this?
Thanks
Unless you are using mutex for inter-process communication, i.e. cancelling your worker thread from another process - I believe there is an easier way to implement a worker thread with cancellation in .net 4.0. You can use a cancellation token, and wait with timeout on it - it will signal if token was cancelled. Complete solution (partially using your code) below:
using System;
using System.Threading;
class App
{
static void Main()
{
var t = new Test();
t.Start();
Thread.Sleep(10000);
Console.WriteLine("aborting");
t.Stop();
}
}
class Test
{
private int threadSleepMinutes = 60;
private Thread serviceThread;
private CancellationTokenSource tokenSource;
public void Start()
{
// Some things renamed to anonymise... you get the idea!
this.tokenSource = new CancellationTokenSource();
this.serviceThread = new Thread(this.BigLoopMethod);
this.serviceThread.IsBackground = true;
this.serviceThread.Start();
}
public void Stop()
{
tokenSource.Cancel();
// Wait 5s max
int timeout = 5000;
if (!serviceThread.Join(timeout))
{
serviceThread.Abort();
}
}
public void BigLoopMethod()
{
try
{
var token = tokenSource.Token;
do
{
int operationsToGo = 4; // Just dummy flags and 'stuff' methods here
while (operationsToGo > 0 && !token.IsCancellationRequested)
{
Console.WriteLine("work");
Thread.Sleep(1000);//DoStuff();
operationsToGo--;
}
Console.WriteLine("no more work");
}
while (!token.WaitHandle.WaitOne(this.threadSleepMinutes * 60000));
}
catch (Exception ex)
{
// Exception handling & logging here
}
}
}
You don't need a "stop" thread. The fact that the start method triggers the BigLoopMethod will be sufficient. All you need in stop is to signal the mutex and then join the thread (Thread.Join() will wait for the thread to halt) with an appropriate timeout. I would recommend for robustness to thread abort if your thread doesn't join within an appropriate time to forcibly kill the service.
So in psuedo code:
void Start()
{
OpenMutex();
TakeMutex();
KickOffMyThread();
}
void Stop();
{
SignalMutex();
if (!MyThread.Join(Timeout))
{
MyThread.Abort();
Environment.Exit(1); // Die as thread won't join
}
}
void MyThread()
{
while (!TakeMutex(sleeptime)
{
DoLongWork();
}
//Thread was signalled, exiting.
}

Monitor.Pulse(this) does not trigger Monitor.Wait(this);

I'm trying to get Monitor.Pulse(this) to trigger Monitor.Wait(this) in my code. I think my Wait statements are all running at some point with no Pulse. I have 5 different threads run by 5 different objects, each representing a queue with different priority. I'm trying to get each thread to run with a certain priority without using the thread priority attribute (i.e. normal, abovenormal, etc.). Anyways, point is that each thread only runs once and then it seems they are stuck at the Monitor.Wait(this) part in the thread that runs for each queue. Does anyone know why the Monitor.Pulse(this) doesn't trigger the Monitor.Wait(this) and continue the cycle. Each thread should be triggered one after the other by the Monitor.Wait(this) and the while loop that uses the Global variable GlobalCount. I think the problem must occur in my Beta method in the first class (Msg class) at the top where this triggering occurs. Or in my main method, although I'm less sure of that part having an issue.
What happens is it will execute a few lines and then start a new line but won't print anything else. The code is still running. I also tried removing the Monitor.Pulse and Monitor.Wait and it partially works, but every time the delta object's beta method runs its thread it is replaced by the alpha method. Does anyone know why this is and how I can get Pulse and Wait to work?
Here is my code (ignore some of the comments):
// StopJoin.cs
using System;
using System.Threading;
using System.Collections;
public class Msg
{
string message;
int priority;
public Msg(string ms, int pr)
{message = ms;
priority = pr;}
// This method that will be called when the thread is started
public void Beta()
{
while(true){
//Console.WriteLine("asdfasdfs");
Console.WriteLine(message+":"+GlobalClass.globalCount);
lock(this) // Enter synchronization block
{
while((priority - 1) != GlobalClass.globalCount){
//Console.WriteLine(GlobalClass.globalCount);
try
{
// Waits for the Monitor.Pulse in WriteToCell
//Console.WriteLine("beginning");
//Monitor.Wait(this);
//Console.WriteLine("end");
}
catch (SynchronizationLockException e)
{
Console.WriteLine(e);
}
catch (ThreadInterruptedException e)
{
Console.WriteLine(e);
}
if(GlobalClass.globalCount >= 5)
GlobalClass.globalCount = 0;
}
Console.WriteLine(message+".Beta is running in its own thread.");
for(int i = 0;i<priority;i++)
{
Console.WriteLine("sending message...");
}
if(GlobalClass.globalCount < 5)
GlobalClass.globalCount = GlobalClass.globalCount + 1;
//Monitor.Pulse(this); // Pulse tells Cell.WriteToCell that
//Console.WriteLine(GlobalClass.globalCount);
}
}
}
}
public class Alpha
{
Msg the_message = new Msg("Alpha",1);
public void doWork()
{the_message.Beta();}
};
public class Charlie
{
Msg the_message = new Msg("Charlie",2);
public void doWork()
{the_message.Beta();}
};
public class Delta
{
Msg the_message= new Msg("Alpha",3);
public void doWork()
{the_message.Beta();}
};
public class Echo
{
Msg the_message= new Msg("Echo",4);
public void doWork()
{the_message.Beta();}
};
public class Foxtrot
{
Msg the_message= new Msg("Foxtrot",5);
public void doWork()
{the_message.Beta();}
};
static class GlobalClass
{
private static int global_count = 0;
public static int globalCount
{
get{return global_count;}
set{global_count = value;}
}
}
public class Simple
{
public static int Main()
{
GlobalClass.globalCount = 2;
long s = 0;
long number = 100000000000000000;
Console.WriteLine("Thread Start/Stop/Join Sample");
Alpha oAlpha = new Alpha();
Charlie oCh = new Charlie();
Delta oDe = new Delta();
Echo oEc = new Echo();
Foxtrot oFo = new Foxtrot();
// Create the thread object, passing in the Alpha.Beta method
// via a ThreadStart delegate. This does not start the thread.
Thread oThread = new Thread(new ThreadStart(oAlpha.doWork));
Thread aThread = new Thread(new ThreadStart(oCh.doWork));
Thread bThread = new Thread(new ThreadStart(oDe.doWork));
Thread cThread = new Thread(new ThreadStart(oEc.doWork));
Thread dThread = new Thread(new ThreadStart(oFo.doWork));
// Start the thread
oThread.Start();
aThread.Start();
bThread.Start();
cThread.Start();
dThread.Start();
// Spin for a while waiting for the started thread to become
// alive:
while (!oThread.IsAlive);
while (!aThread.IsAlive);
while (!bThread.IsAlive);
while (!cThread.IsAlive);
while (!dThread.IsAlive);
// Put the Main thread to sleep for 1 millisecond to allow oThread
// to do some work:
Thread.Sleep(1);
// Wait until oThread finishes. Join also has overloads
// that take a millisecond interval or a TimeSpan object.
oThread.Join();
aThread.Join();
bThread.Join();
cThread.Join();
dThread.Join();
Console.WriteLine();
Console.WriteLine("Alpha.Beta has finished");
/*
try
{
Console.WriteLine("Try to restart the Alpha.Beta thread");
oThread.Start();
}
catch (ThreadStateException)
{
Console.Write("ThreadStateException trying to restart Alpha.Beta. ");
Console.WriteLine("Expected since aborted threads cannot be restarted.");
}
*/
while(s<number)
s++;
// Request that oThread be stopped
oThread.Abort();
aThread.Abort();
bThread.Abort();
cThread.Abort();
dThread.Abort();
return 0;
}
}
I can see a number of problems with your code, but there are two main ones that will be affecting you. I've assumed that your commented out Monitor calls shouldn't be commented (else the code makes no sense).
Firstly, you create a new instance of Msg under each thread. The Beta method locks on the current instance of Msg (in the commented Monitor.Wait(this)), and so each instance is essentially waiting on itself - which will be an infinite wait, because the only Monitor.Pulse is later in the same method, and will never be reached.
Because some of your Msg instances will be created with a higher value for priority, they will skip the while loop entirely and should continue to call Monitor.Pulse, but there will be nothing waiting on that pulse.
Later in your Main method, you have the following:
while (!oThread.IsAlive) ;
while (!aThread.IsAlive) ;
while (!bThread.IsAlive) ;
while (!cThread.IsAlive) ;
while (!dThread.IsAlive) ;
This is flawed. Because there's no guarantee of the execution order of your threads, it's entirely possible for the above code to deadlock. If your oThread isn't started immediately, but dThread is scheduled and runs to completion, you could easily see a case where dThread is completed and "dead" before the final line above is reached.
All in all, I'm not clear on what your code is trying to achieve, but as it stands I'd expect it to deadlock every time.

Categories