I have implemented my custom ThreadManager which has been working flawlessly during my tests. When user wants to close the application, the closing is suspended until all threads exit or they select to end the application without waiting (after 30 seconds have passed).
What I need to clarify is if using Application.DoEvents() could be dangerous in FormClosing event. Shall I use it or find another way of waiting for threads to exit?
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
// save settings before exit
Properties.Settings.Default.Save();
// Update program about intention
Program.ApplicationClosing = true;
try
{
// Inform user with friendly message
ShowModalWaitForm("Application is closing.");
// Keep the timestamp in order to keep track of how much time has passed since form closing started
DateTime startTime = DateTime.Now;
// Wait for all threads to die before continuing or ask user to close by force after 30 seconds have passed
// In case user prefers to wait the timer is refreshed
int threadsAlive;
do
{
if (_threadManager.TryCountAliveThreads(out threadsAlive) && threadsAlive > 0)
{
Application.DoEvents();
Thread.Sleep(50);
}
TimeSpan timePassed = DateTime.Now - startTime;
if (timePassed.Seconds > 30)
{
if (ShouldNotWaitThreadsToExit())
{
break; // Continue with form closing
}
else
{
startTime = DateTime.Now; // Wait more for threads to exit
}
}
} while (threadsAlive > 0);
}
catch (Exception ex)
{
_logger.ErrorException("MainForm_FormClosing", ex);
}
finally
{
HideWaitForm();
}
}
private bool ShouldNotWaitThreadsToExit()
{
return MessageBox.Show(#"Press ""OK"" to close or ""Cancel"" to wait.", "Application not responding ", MessageBoxButtons.OKCancel) == DialogResult;
}
I'd recommend putting your wait condition in another thread. Display a modal dialog from OnFormClosing method. Inside this dialog start worker thread e.g using BackGroundWorker class and dismiss this dialog when waiting finished.
Bonus topic possible drawbacks of calling Application.DoEvents Method
Related
My timer routine is protected from multi-entrance like below:
private void TimerCallback(object state)
{
if (Interlocked.CompareExchange(ref currentlyRunningTasksCount, 1, 0) != 0)
{
return;
}
// some job is being done here
Interlocked.Decrement(ref currentlyRunningTasksCount);
}
There is also timer shutdown procedure, where I want to make sure that the timer is not in the middle of something. So I do the following:
public void Shutdown()
{
aTimer.Change(Timeout.Infinite, Timeout.Infinite);
for(;;)
{
if (Interlocked.CompareExchange(ref currentlyRunningTasksCount, 0, 0) == 0)
{
break;
}
else
{
Thread.Sleep(1000);
continue;
}
}
}
Question - is it proper way to check in Shutdown or I should use Interlock.Read and compare with 0?
I'd use a lock for that. It supports both cases easily and without busy waiting.
You can use if (!Monitor.TryEnter(...)) return; to exit the tick handler.
To wait for the timer to exit you do lock (...) { }.
You need to set a boolean field to signal that the timer is shut down. There can be arbitrarily many tick events be fired after the call to Change. (For the same reason your existing Shutdown code does not actually shut down the timer.)
All of this can be simplified away, though:
async Task RunMyTimerAgent() {
while (true) {
await Task.Delay(...);
Work();
ThrowIfCancelled();
}
}
var timer = RunMyTimerAgent();
//cancel the timer here
timer.Wait(); //Wait till shutdown.
This loop has time drift, though.
I'm currently making a program to simulate a set of ATMs in visual C#. It's supposed to stop somebody accessing their account if it has already been accessed from a different location. Is it possible to show a message that the account has already been accessed while a semaphore is waiting?
Here is the part of the code where the semaphore is used:
private void button1_Click(object sender, EventArgs e)
{
count++;
if (count == 1)
{
account = findAccount();
if (findAccount() != 5)
{
textBox1.Text = "Please Enter Your Pin";
}
else
{
textBox1.Text = "Please Enter Your Account Number";
count = 0;
}
textBox2.Clear();
}
if (count == 2)
{
if (findPin(account) == true)
{
semaphore.WaitOne();
textBox1.Text = "1: Take Out Cash \r\n2: Balance \r\n3: Exit";
}
else
{
semaphore.Release();
textBox1.Text = "Please Enter Your Account Number";
count = 0;
}
textBox2.Clear();
}
if (count == 3)
{
atm();
}
if (count == 4)
{
withdraw();
}
if (count == 5)
{
int value = Convert.ToInt32(textBox2.Text);
customWithdrawl(value);
}
}
Consider doing two calls to WaitOne. The first call will have a timeout of zero and return a bool that will tell you whether or not you got the semaphore, or someone else still owns it. Two things can happen from there:
1) If someone else owns it, pop up a message that says "Someone else owns the semaphore" and call WaitOne again, but without a timeout (like you're doing now). After the 2nd call to WaitOne returns, close the window that you popped up a second ago..
2) If your call to waitOne with 0 timeout returns true, then you got the semaphore on the 1st try. No need to pop up a window.
Example:
if( semaphore.WaitOne(0) ) //This returns immediately
{
//We own the semaphore now.
DoWhateverYouNeedToDo();
}
else
{
//Looks like someone else already owns the semaphore.
PopUpNotification();
semaphore.WaitOne(); //This one will block until the semaphore is available
DoWhateverYouNeedToDo();
CloseNotification();
}
semaphore.Release();
Note, there are some other issues lurking here.
You probably want to use a try/finally block to release the semaphore to ensure that it gets released across all exception paths.
It's also probably a bad idea to call semaphore.WaitOne() from the GUI thread because the application will become non-responsive while it waits. In fact, you may not see the result of PopUpNotification() if you've hung the GUI thread while doing the 2nd Wait. Consider doing the long wait on a 2nd thread and raising an event back on the GUI thread once you own the semaphore
Consider the following design to resolve Issue 2:
private void button1_Click(object sender, EventArgs e)
{
if(AcquireSemaphoreAndGenerateCallback())
{
//Semaphore was acquired right away. Go ahead and do whatever we need to do
DoWhateverYouNeedToDo();
semaphore.Release()
}
else
{
//Semaphore was not acquired right away. Callback will occur in a bit
//Because we're not blocking the GUI thread, this text will appear right away
textBox1.Text = "Waiting on the Semaphore!";
//Notice that the method returns right here, so the GUI will be able to redraw itself
}
}
//This method will either acquire the semaphore right away and return true, or
//have a worker thread wait on the semaphore and return false. In the 2nd case,
//"CallbackMethod" will run on the GUI thread once the semaphore has been acquired
private void AcquireSemaphoreAndGenerateCallback()
{
if( semaphore.WaitOne(0) ) //This returns immediately
{
return true; //We have the semaphore and didn't have to wait!
}
else
{
ThreadPool.QueueUserWorkItem(new WaitCallback(Waiter));
return false; //Indicate that we didn't acquire right away
}
}
//Wait on the semaphore and invoke "CallbackMethod" once we own it. This method
//is meant to run on a background thread.
private void Waiter(object unused)
{
//This is running on a separate thread
Semaphore.WaitOne(); //Could take a while
//Because we're running on a separate thread, we need to use "BeginInvoke" so
//that the method we're calling runs on the GUI thread
this.BeginInvoke(new Action(CallbackMethod));
}
private void CallbackMethod()
{
textBox1.Text = string.Empty; //Get rid of the "Waiting For Semaphore" text. Can't do this if we're not running on the GUI thread
DoWhateverYouNeedToDo();
semaphore.Release();
}
Now, this solution could also be fraught with peril. It's kind of hard to follow the execution of the program because it jumps around from method to method. If you have an exception, it could be difficult to recover from and make sure all of your program state is correct. You also have to keep track of things like the account number and the pin numbers through all of these method calls. In order to do that, Waiter and CallbackMethod should probably take some parameter that tracks this state that gets passed along to each step. There's also no way to abort waiting (a time out). It will probably work, but shouldn't make it into any production code because it would be too difficult to maintain or extend.
If you really wanted to do it right, you should consider encapsulating the ATM logic in an object that will raise events that the GUI can subscribe to. You could have a method like ATM.LogInAsync(Account,Pin) that you could call. This method would return immediately, but some time later, an event on the ATM class like "LogInComplete" would fire. This event would have a custom EventArgs object that would contain data to trace which log-in has occurred (mainly the Account number). This is called the Event-based Asynchronous Pattern
Alternatively, if you're using C# 5.0, you can use the new Async/Await syntax in the AcquireSemaphoreAndGenerateCallback() method. That's probably the easiest way because the compiler will handle most of the complexities for you
Yes, you may show your message/form/messagebox right before the Wait method. Then when it receives the signal to unblock, you hide your message.
I'm researching this and a confused by how to execute this as cleanly as possible. The idea is that I have a thread pause for a designated amount of seconds before it continues with the logic, all the while having other threads operate with the UI.
Here is as far as I've gotten but it does not work and throws errors:
public static int f_sleep(int time)
{
/*
System.DateTime now = System.DateTime.Now;
System.TimeSpan duration = new System.TimeSpan(0, 0, 0, time);
System.DateTime then = now.Add(duration);
while (then > DateTime.Now)
{
Application.DoEvents();
Thread.Sleep(10);
}
while (session.automation_lock == true)
{
Thread.Sleep(6000);
}
if (session.automation_stop == true)
{
Thread.CurrentThread.Abort();
}
GC.Collect();
GC.WaitForPendingFinalizers();
*/
System.Threading.Timer timer = new System.Threading.Timer(o => f_sleep_timer_tick(timer), null, time, -1);
timer.start();
return 1;
}
public static void f_sleep_timer_tick(System.Threading.Timer timer)
{
timer.Stop();
}
Notice I commented out my old way of handling breaks because I believe it's causing hangups. What do you think about my old way? Also, you can see I'm trying to define a timer, and send it over into the tick method so I don't have to define a global timer. But I know I'm doing it wrong. I just want the timer to tick once (a tick being x second) and then continue with the threads logic by f_sleep returning the 1 telling the thread's logic it's ok now to continue.
I have a winform app, which shows some information in time, every time it loads the data, I set a delay time of 7 sec like this: System.Threading.Thread.Sleep(7000) so the info can be viewed. I want to have a buttom that allows me to jump to the next information without waiting.
The logic I use is as follows: get Information, if any, wait 7 sec, next data, and so on. So if I press the button I'd like to set that time to 0.
Is there any way to cancel the waiting period?
here is the code:
ManualResetEvent wait_handle = new ManualResetEvent(true);
{...}
private void TheLoop(object stateinfo)
{
bool hasInfo = true;
bool hasLines = GetLinesOnProduction();
while (doLoop)
{
wait_handle.WaitOne();
if (hasLines)
{
param1 = Lines[CurrentLine].line;
param2 = Lines[CurrentLine].WO;
//Here I query the DB for the CurrentLine Data
ShowLineInformation(CurrentLine);
ShowChartByHour(param1, param2, out hasInfo);
if (hasInfo)
System.Threading.Thread.Sleep(7000);
//Here I move to the next line
if (CurrentLine < Lines.Count - 1)
CurrentLine++;
else
{
CurrentLine = 0; //Start all over again
hasLines = GetLinesOnProduction();
}
}
else
{
System.Threading.Thread.Sleep(40000); //(No Lines)Wait to query for lines again
hasLines = GetLinesOnProduction();
}
}
}
private void btnPauseResume_Click(object sender, EventArgs e)
{
if (btnPauseResume.Text == "Pause")
{
btnPauseResume.Text = "Resume";
wait_handle.Reset();
}
else
{
btnPauseResume.Text = "Pause";
wait_handle.Set();
}
}
Instead of doing Thread.Sleep, you can use a wait event, and simply set it to cancel the wait. Something like this:
var waiter = new AutoResetEvent(false);
bool wasCanceled = waiter.WaitOne(7000);
if(wasCanceled)
// Jump to next...
// Cancel the wait from another thread
waiter.Set()
Rather than using Thread.Sleep, which will suspend all activity in your UI, use a timer instead. With a timer, the UI can still response to events while your timer callback is pending, and when you click the button, you can cancel the timer.
I would set up the delay by locking an object and then executing a Monitor.Wait on with a delay of 7 seconds. Then, from the form, when the button is pushed, lock the object and do a Monitor.PulseAll.
You could use a ManualResetHandle:
// Declare it as class member
ManualResetHandle _manualResetHandle = new ManualResetHandle();
// Wait in your process for seven seconds, or until it is Set()
_manualResetHandle.WaitOne(7000);
// Set() it in your click event handler:
_manualResetHandle.Set();
I have this code to pause and resume a thread:
public partial class frmMain : Form
{
(...)
ManualResetEvent wait_handle = new ManualResetEvent(true);
(...)
}
private void frmMain_Shown(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(TheLoop));
}
private void TheLoop(object stateinfo)
{
bool hasInfo = true;
while (doLoop)
{
wait_handle.WaitOne();
bool hasLines = GetInfo();
if (hasLines)
{
//Consuming time Operation 1
System.Threading.Thread.Sleep(7000);
if (CurrentLine < line.Count - 1)
CurrentLine++;
else
{
bool hasInfo2 = GetInfo2();
if (hasInfo2)
{
//Consuming time Operation 2
System.Threading.Thread.Sleep(7000);
}
CurrentLine = 0;
}
}
else
System.Threading.Thread.Sleep(40000); //Wait to query again
}
}
private void btnPauseResume_Click(object sender, EventArgs e)
{
if (btnPauseResume.Text == "Pause")
{
btnPauseResume.Text = "Resume";
wait_handle.Reset();
}
else
{
btnPauseResume.Text = "Pause";
wait_handle.Set();
}
}
The code above shows a cycle information, it works find to pause and resume the "first consuming time operation" but doesn't work for the second one, if I press the button to pause the thread in the second consuming time operation, this one continues and when the first one appears again, then it pauses there.
What am I missing here?
Thx
Have you considered using a Background Worker instead since you are using WinForms? It would probably be easier than trying to 'Pause' a thread. You can check the CancellationPending property to see if a user has elected to cancel the operation. The link has a good sample to look at.
I have never seen someone pausing a thread. Create a delegate and event inside the class or method that you are executing on a separate threat. Execute that event whenever you wish to pause your thred.
There is not any reason that I can see that would prevent a second call to WaitOne from working if placed before the 2nd time consuming operation. Since you are using a ManualResetEvent the wait handle's state will persist until either Set or Reset is called. That means if you resume the thread by calling Set then both calls to WaitOne will pass through. Likewise, if you pause the thread by calling Reset then both calls to WaitOne will block. Of course, it will not be possible to predict where the worker thread will pause if there is more than one call to WaitOne.
Got it guys! the thing is where you put the WaitOne(). For instance, if I have a While Loop (like my example) if I put the wait before it, no matter how many times I hit the pause button, it won't stop the thread, it's logic since the loop already began, but if I put it at the end, then it will work.
Appreciated your help.