I'm a Java programmer who has been asked to make some changes to C# applications. I've been working with C# for a week now, and I've finally hit a point where looking at the documentation isn't helping and I can't find solutions when I google.
In this case I have a Windows Service that processes messages that arrive in a MSMQ. When a message is received the currently listening thread picks it up and goes off to do an operation that takes a couple of seconds.
public void Start()
{
this.listen = true;
for (int i = 0; i < Constants.ThreadMaxCount; i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(this.StartListening), i);
}
...
private void StartListening(Object threadContext)
{
int threadId = (int)threadContext;
threads[threadId] = Thread.CurrentThread;
PostRequest postReq;
while(this.listen)
{
System.Threading.Monitor.Enter(locker);
try
{
postReq = GettingAMessage();
}
finally
{
System.Threading.Monitor.Exit(locker);
}
}
...
}
GettingAMessage() has the following lines that listen for a message:
Task<Message> ts = Task.Factory.FromAsync<Message>
(queue.BeginReceive(), queue.EndReceive);
ts.Wait();
The problem is, when the Stop() method is called and there are no messages going into the MSMQ the threads all sit there waiting for a message. I have tried using timeouts, but that method doesn't seem elegant to me(and having switched over to the Task Factory, I'm not sure how to implement them currently). My solution to this was to add a reference of each thread to an array, so that I could cancel them. The following is called by each worker thread after being created.
threads[threadId] = Thread.CurrentThread;
and then supposed to be aborted by
public void Stop()
{
try
{
this.listen = false;
foreach(Thread a in threads) {
a.Abort();
}
}
catch
{...}
}
Any advice on why this isn't shutting the threads down? (Or even better, can anyone tell me where I should look for how to cancel the ts.Wait() properly?)
Use the ManualResetEvent class to achieve a proper & graceful stopping of your running threads.
In addition, don't use the ThreadPool for long running threads, use your own created threads, otherwise, with lots of long-running tasks, you could end up with thread-pool starvation, possibly even leading to deadlock:
public class MsmqListener
{
privatec ManualResetEvent _stopRequested = new ManualResetEvent(false);
private List<Thread> _listenerThreads;
private object _locker = new _locker();
//-----------------------------------------------------------------------------------------------------
public MsmqListener
{
CreateListenerThreads();
}
//-----------------------------------------------------------------------------------------------------
public void Start()
{
StartListenerThreads();
}
//-----------------------------------------------------------------------------------------------------
public void Stop()
{
try
{
_stopRequested.Set();
foreach(Thread thread in _listenerThreads)
{
thread.Join(); // Wait for all threads to complete gracefully
}
}
catch( Exception ex)
{...}
}
//-----------------------------------------------------------------------------------------------------
private void StartListening()
{
while( !_stopRequested.WaitOne(0) ) // Blocks the current thread for 0 ms until the current WaitHandle receives a signal
{
lock( _locker )
{
postReq = GettingAMessage();
}
...
}
//-----------------------------------------------------------------------------------------------------
private void CreateListenerThreads()
{
_listenerThreads = new List<Thread>();
for (int i = 0; i < Constants.ThreadMaxCount; i++)
{
listenerThread = new Thread(StartListening);
listenerThreads.Add(listenerThread);
}
}
//-----------------------------------------------------------------------------------------------------
private void StartListenerThreads()
{
foreach(var thread in _listenerThreads)
{
thread.Start();
}
}
}
UPDATE:
I changed the use of AutoResetEvent with ManualResetEvent in order to support the stopping of multiple waiting threads (Using ManualResetEvent, once you signaled, all waiting threads will be notified and be free to proceed theirs job - stop pooling for messages, in your case).
Using volatile bool does not provide all the guaranties. It may still read stale data. Better to use underlying OS synchronisation mechanism as it provides much stronger guaranties. Source: stackoverflow.com/a/11953661/952310
Related
The code below is an example on multi-threading that the prof presented in class. I am new to coding (first course). I have read on multi-threading and using locks. Reading the theory is fun. var fun = Theory.Read(multi-threading); Actually coding threads and locks seems to baffle me.
Trying to understand how the two threads in the code below will behave. From testing the code it looks like lock1 will not release and message2 is not enqueue-ed, but I might be wrong. Looks like there is a synchronization issue. Is this an example of a deadlock?
I am also wondering why locks and threads are required if two different queues are used. I am not seeing a shared resource.
Is there a way to fix this code to prevent the synchronization issue?
private static object Lock1 = new object(); // Protect MessageQueueOne
private static object Lock2 = new object(); // Protect MessageQueueTwo
private static Queue<string> MessageQueueOne = new Queue<string>();
private static Queue<string> MessageQueueTwo = new Queue<string>();
private static void AddMessages(string message1, string message2)
{
lock (Lock1)
{
// (1) Thread 1 is here...
MessageQueueOne.Enqueue(message1);
lock (Lock2)
{
MessageQueueTwo.Enqueue(message2);
}
}
}
private static void RemoveMessages()
{
lock (Lock2)
{
if (MessageQueueTwo.Count > 0)
{
// (2) Thread 2 is here...
Console.WriteLine(MessageQueueTwo.Dequeue());
}
lock (Lock1)
{
if (MessageQueueOne.Count > 0)
{
Console.WriteLine(MessageQueueOne.Dequeue());
}
}
}
}
private static void Main()
{
Task taskOne = Task.Run(() =>
{
for (int i = 0; i < 100; ++i)
{
AddMessages($"Message One: {DateTime.Now}", $"Message Two: {DateTime.UtcNow}");
Thread.Sleep(25);
}
});
Task taskTwo = Task.Run(() =>
{
for (int i = 0; i < 100; ++i)
{
RemoveMessages();
Thread.Sleep(25);
}
});
taskOne.Wait();
taskTwo.Wait();
Console.Write("Tasks are finished");
Console.ReadKey();
}
The code in the post is classical example of deadlock and expected to deadlock most of the time. See more links in Wikipedia article on deadlocks.
What leads to deadlock: one thread locks "lock1" and waits for "lock2", the other thread at the same time holds lock on "lock2" and will release it after acquiring "lock1" which will never be release by waiting thread.
Standard solutions
listen to your class to know the answer
read existing examples
if above fails - one option is to acquire resources in fixed order (i.e. if need to lock on more than one resource get "lock1" first, than "lock2" and so on) for all thread (Would you explain lock ordering?).
I have a situation that i export data to a file and what i have been asked to do is to provide a cancel button which on click will stop the export if it takes too much time to export.
I started exporting to the file in a thread. And i try to abort the thread on the button click. But it do not work.
I searched on Google and i found that abort() is not recommended. But what else should I choose to achieve it?
My current code is:
private void ExportButtonClick(object param)
{
IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
DataTable dtData = ExportHelper.ToDataTable(data);
thread = new Thread(new ThreadStart(()=>ExportHelper.DataTableToCsv(dtData, "ExportFile.csv")));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Name = "PDF";
thread.Start();
}
private void StopButtonClick(object param)
{
if (thread.Name == "PDF")
{
thread.Interrupt();
thread.Abort();
}
}
Aborting a thread is a bad idea, especially when dealing with files. You won't have a chance to clean up half-written files or clean-up inconsistent state.
It won't harm the .NET Runtime bat it can hurt your own application eg if the worker method leaves global state, files or database records in an inconsistent state.
It's always preferable to use cooperative cancellation - the thread periodically checks a coordination construct like a ManualResetEvent or CancellationToken. You can't use a simple variable like a Boolean flag, as this can lead to race conditions, eg if two or more threads try to set it at the same time.
You can read about cancellation in .NET in the Cancellation in Managed Threads section of MSDN.
The CancellationToken/CancellationTokenSource classes were added in .NET 4 to make cancellation easier that passing around events.
In your case, you should modify your DataTableToCsv to accept a CancellationToken. That token is generated by a CancellationTokenSource class.
When you call CancellationTokenSource.Cancel the token's IsCancellationRequested property becomes true. Your DataTableToCsv method should check this flag periodically. If it's set, it should exit any loops, delete any inconsistent files etc.
Timeouts are directly supported with CancelAfter. Essentially, CancelAfter starts a timer that will fire Cancel when it expires.
Your code could look like this:
CancellationTokenSource _exportCts = null;
private void ExportButtonClick(object param)
{
IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
DataTable dtData = ExportHelper.ToDataTable(data);
_exportCts=new CancellationTokenSource();
var token=_exportCts.Token;
thread = new Thread(new ThreadStart(()=>
ExportHelper.DataTableToCsv(dtData, "ExportFile.csv",token)));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Name = "PDF";
_exportCts.CancelAfter(10000);
thread.Start();
}
private void StopButtonClick(object param)
{
if (_exportCts!=null)
{
_exportCts.Cancel();
}
}
DataTableToCsv should contain code similar to this:
foreach(var row in myTable)
{
if (token.IsCancellationRequested)
{
break;
}
//else continue with processing
var line=String.Join(",", row.ItemArray);
writer.WriteLine(line);
}
You can clean up your code quite a bit by using tasks instead of raw threads:
private async void ExportButtonClick(object param)
{
IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
DataTable dtData = ExportHelper.ToDataTable(data);
_exportCts=new CancellationTokenSource();
var token=_exportCts.Token;
_exportCts.CancelAfter(10000);
await Task.Run(()=> ExportHelper.DataTableToCsv(dtData, "ExportFile.csv",token)));
MessageBox.Show("Finished");
}
You could also speed it up by using asynchronous operations, eg to read data from the database or write to text files without blocking or using threads. Windows IO (both file and network) is asynchronous at the driver level. Methods like File.WriteLineAsync don't use threads to write to a file.
Your Export button handler could become :
private void ExportButtonClick(object param)
{
IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
DataTable dtData = ExportHelper.ToDataTable(data);
_exportCts=new CancellationTokenSource();
var token=_exportCts.Token;
_exportCts.CancelAfter(10000);
await Task.Run(async ()=> ExportHelper.DataTableToCsv(dtData, "ExportFile.csv",token)));
MessageBox.Show("Finished");
}
and DataTableToCsv :
public async Task DataTableToCsv(DataTable table, string file,CancellationToken token)
{
...
foreach(var row in myTable)
{
if (token.IsCancellationRequested)
{
break;
}
//else continue with processing
var line=String.Join(",", row.ItemArray);
await writer.WriteLineAsync(line);
}
You can use a boolean flag. Use a volatile boolean for that.
In the helper do something like:
this.aborted = false;
while(!finished && !aborted) {
//process one row
}
Whenever you want to cancel the operation, you call a method to set aborted to true:
public void Abort() {
this.aborted = true;
}
Have a read here: https://msdn.microsoft.com/en-us/library/system.threading.threadabortexception(v=vs.110).aspx
When a call is made to the Abort method to destroy a thread, the common language runtime throws a ThreadAbortException. ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block. When this exception is raised, the runtime executes all the finally blocks before ending the thread. Because the thread can do an unbounded computation in the finally blocks or call Thread.ResetAbort to cancel the abort, there is no guarantee that the thread will ever end. If you want to wait until the aborted thread has ended, you can call the Thread.Join method. Join is a blocking call that does not return until the thread actually stops executing.
Since Thread.Abort() is executed by another thread, it can happen anytime and when it happens ThreadAbortException is thrown on target thread.
Inside ExportHelper.DataTableToCsv:
catch(ThreadAbortException e) {
Thread.ResetAbort();
}
On StopButtonClick
if (thread.Name == "PDF")
{
thread.Interrupt();
thread.Join();
}
To Stop a thread you have one option of Thread.Abort.However because this method thrown ThreadAbortException on the target thread when it executed by another thead.
Which is not recommended.
The second option to stop a thread is by using shared variable that both your target and your calling thread can access.
See the Example ::
public static class Program
{
public static void ThreadMethod(object o)
{
for (int i = 0; i < (int)o; i++)
{
Console.WriteLine("ThreadProc: { 0}", i);
Thread.Sleep(0);
}
}
public static void Main()
{
bool stopped = false;
Thread t = new Thread(new ThreadStart(() =>
{
while (!stopped)
{
Console.WriteLine("Running...");
Thread.Sleep(1000);
}
}));
t.Start();
Console.WriteLine("Press any key to exit");
Console.ReadKey();
stopped = true;
t.Join();
}
}
//Source :: Book --> Programming in c#
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.
}
I have a Thread (STAThread) in a Windows Service, which performs a big amount of work. When the windows service is restarted I want to stop this thread gracefully.
I know of a couple of ways
A volatile boolean
ManualResetEvent
CancellationToken
As far as I have found out Thread.Abort is a no go...
What is the best practice ?
The work is perfomed in another class than the one where the thread is started, so it is necessary to either introduce a cancellationToken parameter in a constructor or for example have a volatile variable. But I just can't figure out what is smartest.
Update
Just to clarify a little I have wrapped up a very simple example of what I'm talking about. As said earlier, this is being done in a windows service. Right now I'm thinking a volatile boolean that is checked on in the loop or a cancellationToken....
I cannot wait for the loop to finish, as stated below it can take several minutes, making the system administrators of the server believe that something is wrong with the service when they need to restart it.... I can without problems just drop all the work within the loop without problems, however I cannot do this with a Thread.Abort it is "evil" and furthermore a COM interface is called, so a small clean up is needed.
Class Scheduler{
private Thread apartmentThread;
private Worker worker;
void Scheduling(){
worker = new Worker();
apartmentThread = new Thread(Run);
apartmentThread.SetApartmentState(ApartmentState.STA);
apartmentThread.Start();
}
private void Run() {
while (!token.IsCancellationRequested) {
Thread.Sleep(pollInterval * MillisecondsToSeconds);
if (!token.IsCancellationRequested) {
worker.DoWork();
}
}
}
}
Class Worker{
//This will take several minutes....
public void DoWork(){
for(int i = 0; i < 50000; i++){
//Do some work including communication with a COM interface
//Communication with COM interface doesn't take long
}
}
}
UPDATE
Just examined performance, using a cancellationToken where the isCancelled state is "examined" in the code, is much faster than using a waitOne on a ManualResetEventSlim. Some quick figuers, an if on the cancellationToken iterating 100.000.000 times in a for loop costs me approx. 500 ms, where the WaitOne costs approx. 3 seconds. So performance in this scenario it is faster to use the cancellationToken.
You haven't posted enough of your implementation but I would highly recommend a CancellationToken if that is available to you. It's simple enough to use and understand from a maintainability standpoint. You can setup cooperative cancellation as well too if you decide to have more than one worker thread.
If you find yourself in a situation where this thread may block for long periods of time, it's best to setup your architecture so that this doesn't occur. You shouldn't be starting threads that won't play nice when you tell them to stop. If they don't stop when you ask them, the only real way is to tear down the process and let the OS kill them.
Eric Lippert posted a fantastic answer to a somewhat-related question here.
I tend to use a bool flag, a lock object and a Terminate() method, such as:
object locker = new object();
bool do_term = false;
Thread thread = new Thread(ThreadStart(ThreadProc));
thread.Start();
void ThreadProc()
{
while (true) {
lock (locker) {
if (do_term) break;
}
... do work...
}
}
void Terminate()
{
lock (locker) {
do_term = true;
}
}
Asides from Terminate() all the other fields and methods are private to the "worker" class.
Use a WaitHandle, most preferably a ManualResetEvent. Your best bet is to let whatever is in your loop finish. This is the safest way to accomplish your goal.
ManualResetEvent _stopSignal = new ManualResetEvent(false); // Your "stopper"
ManualResetEvent _exitedSignal = new ManualResetEvent(false);
void DoProcessing() {
try {
while (!_stopSignal.WaitOne(0)) {
DoSomething();
}
}
finally {
_exitedSignal.Set();
}
}
void DoSomething() {
//Some work goes here
}
public void Terminate() {
_stopSignal.Set();
_exitedSignal.WaitOne();
}
Then to use it:
Thread thread = new Thread(() => { thing.DoProcessing(); });
thread.Start();
//Some time later...
thing.Terminate();
If you have a particularly long-running process in your "DoSomething" implementation, you may want to call that asynchronously, and provide it with state information. That can get pretty complicated, though -- better to just wait until your process is finished, then exit, if you are able.
There are two situations in which you may find your thread:
Processing.
Blocking.
In the case where your thread is processing something, you must wait for your thread to finish processing in order for it to safely exit. If it's part of a work loop, then you can use a boolean flag to terminate the loop.
In the case where your thread is blocking, then you need to wake your thread and get it processing again. A thread may be blocking on a ManualResetEvent, a database call, a socket call or whatever else you could block on. In order to wake it up, you must call the Thread.Interrupt() method which will raise a ThreadInterruptedException.
It may look something like this:
private object sync = new object():
private bool running = false;
private void Run()
{
running = true;
while(true)
{
try
{
lock(sync)
{
if(!running)
{
break;
}
}
BlockingFunction();
}
catch(ThreadInterruptedException)
{
break;
}
}
}
public void Stop()
{
lock(sync)
{
running = false;
}
}
And here is how you can use it:
MyRunner r = new MyRunner();
Thread t = new Thread(()=>
{
r.Run();
});
t.IsBackground = true;
t.Start();
// To stop the thread
r.Stop();
// Interrupt the thread if it's in a blocking state
t.Interrupt();
// Wait for the thread to exit
t.Join();
I have the scenario where a command comes in over a socket which requires a fair amount of work. Only one thread can process the data at a time. The commands will come in faster than can process it. Over time there will be quiet a back log.
The good part is that I can discard waiting threads and really only have to process the last one that is waiting - (or process the first one in and discard all the other once). I was thinking about using a semaphore to control the critical section of code and to use a boolean to see if there are any threads blocking. If there are blocking thread I would just discard the thread.
My mind is drawing a blank on how to implement it nicely I would like to implement it with out using an integer or boolean to see if there is a thread waiting already.
I am coding this in c#
You can use Monitor.TryEnter to see whether a lock is already taken on an object:
void ProcessConnection(TcpClient client)
{
bool lockTaken = false;
Monitor.TryEnter(lockObject, out lockTaken);
if (!lockTaken)
{
client.Close();
return;
}
try
{
// long-running process here
}
finally
{
Monitor.Exit(lockObject);
client.Close();
}
}
Note that for this to work you'll still have to invoke the method in a thread, for example:
client = listener.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(notused => ProcessConnection(client));
FYI, the lock statement is just sugar for:
Monitor.Enter(lockObject);
try
{
// code within lock { }
}
finally
{
Monitor.Exit(lockObject);
}
I believe you are looking for the lock statement.
private readonly object _lock = new object();
private void ProccessCommand(Command command)
{
lock (_lock)
{
// ...
}
}
It sounds like you just need to use the lock statement. Code inside a lock statement will allow only one thread to work inside the code block at once.
More info: lock Statement
From the sounds of what you've posted here, you might be able to avoid so many waiting threads. You could queue up the next command to execute, and rather than keep the threads waiting, just replace the command to execute next after the current command finishes. Lock when replacing and removing the "waiting" command.
Something like this:
class CommandHandler
{
Action nextCommand;
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
object lockObject = new object();
public CommandHandler()
{
new Thread(ProcessCommands).Start();
}
public void AddCommand(Action nextCommandToProcess)
{
lock (lockObject)
{
nextCommand = nextCommandToProcess;
}
manualResetEvent.Set();
}
private void ProcessCommands()
{
while (true)
{
Action action = null;
lock (lockObject)
{
action = nextCommand;
nextCommand = null;
}
if (action != null)
{
action();
}
lock (lockObject)
{
if(nextCommand != null)
continue;
manualResetEvent.Reset();
}
manualResetEvent.WaitOne();
}
}
}
check out: ManualResetEvent
It's a useful threading class.