Application CTRL-C signal works only once - c#

I am having a rather weird problem I've been unable to Google out.
Working on some barely-sensical school demo program, I'm required to have a worker loop go about it's business, until it's interrupted by a CTRL-C signal, which then runs some other function. When that's done, the program resumes it's previous worker loop where it left off.
All that works beautifully, the new keypress event is done like this:
Console.TreatControlCAsInput = false;
Console.CancelKeyPress += new ConsoleCancelEventHandler(interrupt);
When CTRL-C is pressed, the "interrupt" method is correctly called and executed... once.
When it's through, I appear to be completely unable to call it again via interrupt signal. Any subsequent CTRL-C keypress is completely ignored.
The "interrupt" method ends with .Join to merge with it's parent thread, since the event handlers are ran on seperate threads for some reason.
What is preventing me from calling the interrupt method as many times as I want?

Thread.Join doesn't "merge" threads. It blocks the current thread until the other thread is finished. As you are joining on the main thread, this will never happen until the program exits.
Just remove that call completely.

Are you setting the Cancel property to true appropriately? This works for me:
using System;
using System.Threading;
class Test
{
static void Main()
{
Console.TreatControlCAsInput = false;
Console.CancelKeyPress += (sender, args) => {
// We want to keep going...
args.Cancel = true;
Console.WriteLine("Handler called");
};
Console.WriteLine("Go for it!");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
Thread.Sleep(1000);
}
}
}
It's not clear what your threading looks like - that could well be another aspect which is incorrect, as per Daniel's answer.

Console.TreatControlCAsInput looks like it should not be set to false:
true if Ctrl+C is treated as ordinary input; otherwise, false.

Related

is it possible to resume the interrupted process after getting SIGTERM singal?

In below program, What I am trying to achieve is when I press Ctrl+C or give SIGTERM then I want to make 'isEnable' false and let the while loop finish executing it's code one last time and exit the program gracefully.
I can achieve this only with CancelKeyPress because EventArgs passed on this handler has Cancel property and I just need to set it true. And, CancelKeyPress only handle Ctrl+C. But, I want same thing when I got SIGTERM signal.
Does anyone know is it possible to resuming the process after getting SIGTERM signal just like in CancelKeyPress?
Please let me know if you need more information to understand my question.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
bool isEnable = true;
Console.CancelKeyPress += (s, e) =>
{
e.Cancel = true;
isEnable = false; // making while loop false
Console.WriteLine("Inside CancelKeyPress");
};
AppDomain.CurrentDomain.ProcessExit += (s, e) =>
{
isEnable = false; // making while loop false
Console.WriteLine("AppDomain ProcessExit!");
};
while (isEnable)
{
Console.WriteLine("first");
Thread.Sleep(3000);
Console.WriteLine("Second");
Thread.Sleep(3000);
Console.WriteLine("Third");
Thread.Sleep(3000);
Console.WriteLine("Fourth");
Thread.Sleep(3000);
}
Console.WriteLine("Gracefull Shotdown");
}
}
SIGTERM is a "polite" way of asking the program to terminate gracefully.
The program can then handle this is any way it wishes.
It can ignore it
It can shut down immediately
It can finish processing the current task, then gracefully
shutdown (disconnect from DB, close files etc).
It can wait as long as it wants and then shutdown.
(If running under Kubernetes then you can configure how long K8s will wait between sending a SIGTERM and a SIGKILL. So if your app needs 10 minutes to shut down then you can configure this and it is considered perfectly acceptable.)
In your code above, you are not "resuming processing" but are choosing to catch and handle this in your program by effectively ignoring the CTRL-C request.
So there is no reason why you can't do this also when receiving a SIGTERM on other platforms.
You might find this post helpful:
https://medium.com/#rainer_8955/gracefully-shutdown-c-apps-2e9711215f6d

C# Speech Recognition Contained in Background Worker still running after canceled

I am having a issue with my program. There is a background worker that starts on button press and inside this worker is a do loop containing some speech recognition code that monitors for user input and displays it. The bug is as followed. If you cancel the worker on stop/start button the speech recognition engine is still waiting for one more input, and if you click the start button before speaking to clear it out it will now output 2 duplicate results instead of one.
A good portion of the speech recognition code was hack slash copy pasted together to get working, so im not intimately familiar with it.
Program Code: http://pastebin.com/tBXKs5DT
any help is appreciated
Thanks
The problem occurs because you loose the cancel reference before your loop reaches the the cancel check. To explain this I'll take a small sample of your code:
_worker.DoWork += new DoWorkEventHandler((state, args) => {
do {
if (_worker.CancellationPending) {
break;
}
//recognize spoken command
} while (true);
});
When you hit the stop button you flag the _worker to cancel. But the loop is most likely waiting for you to speek up. Only after you've spoken the loop will continue and check the cancellation flag.
However: When you press start the _worker reference is overwritten and reused. At that point you got two loops running. Both looking at the same cancellation flag which is set back to false. So if you then speak up both will process your voice command, see that they do not have to cancel and carry on.
To resolve this your stop button should interrupt the worker thread by calling the .Interrupt on the _worker in your stop event (button2_click). Then inside the do work code you need to have a try/catch for a interrupt exception. If the exception is triggered then check if the cancel flag is true, if so then break out of the while loop.
A few extra side notes:
There is a easier way to listen to voice commands using the RecognizeAsync. Have a look at this example on msdn
File.Exists already returns a bool. There is no need to compare it to true in your if statements
When writing more then a 'hello world' program try to give everything a proper name. The code becomes quite the treasure hunt with names like "button1"
EDIT:
A example for the interrupt (Disclaimer: I don't have VS at hand here, so typos might be included)
Setting the cancel flag and interrupting a thread is quite simple. Assuming that your thread (or background worker which is basically a thread) is called _worker it looks somewhat like this:
_worker.CancelAsync();
_worker.Interrupt();
This will trigger a interrupt exception inside that dowork event. Be sure that these exceptions are handled. In your case that would look something like this:
_worker.DoWork += new DoWorkEventHandler((state, args) => {
do {
try {
if (_worker.CancellationPending) {
break;
}
//recognize spoken command
} catch (ThreadInterruptedException) {
if (_worker.CancellationPending) {
break;
}
//reaching this would be a odd situation. It means you interrupted your thread without canceling it.
//In your case this shouldn't happen, but there are cases in which you would want to support this
}
} while (true);
});

Why is SendKeys.SendWait and Thread.Sleep inside Timer_Tick event not blocking?

I have a single Timer control on my form with the following Tick event handler:
private void timer1_Tick(object sender, EventArgs e) {
foreach (char c in "Andrew") {
SendKeys.SendWait(c.ToString());
System.Threading.Thread.Sleep(1000);
}
}
Since System.Windows.Forms.Timer runs on the UI thread, I would expect the event handler to block further Tick events occuring while it's running which would give AndrewAndrewAndrew... as output. Instead, I get AnAnAnAn....
Why are subsequent Tick events raised and handled before the first has finished?
How can I make sure the Timer raises one event at a time, and gets fully blocked until the handler has run to completion?
I realise Thread.Sleep is a horrible way of timing code. I just want to know what is going on.
You are victim of re-entry via the message loop. You are recursing into your timer1_Tick function indirectly via the message loop. What is happening is that inside SendKeys.SendWait another message loop is being spun up (not on a different thread) to monitor the whether the messages have been processed. Then on another thread, while messages are being processed in this inner loop, the timer is firing and posting a message to call to your tick function again. Hilarity ensues.
Perhaps a simplified example will help. Run this and observe the output.
public class Program
{
private static Queue<Action> queue = new Queue<Action>();
public static void Main(string[] args)
{
// put three things in the queue.
// In a simple world, they would finish in order.
queue.Enqueue(() => DoWork(1));
queue.Enqueue(() => DoComplicatedWork(2));
queue.Enqueue(() => DoWork(3));
PumpMessages();
}
private static void PumpMessages()
{
while (queue.Count > 0)
{
Action action = queue.Dequeue();
action();
}
}
private static void DoWork(int i)
{
Console.WriteLine("Doing work: {0}", i);
}
private static void DoComplicatedWork(int i)
{
Console.WriteLine("Before doing complicated work: {0}", i);
PumpMessages();
Console.WriteLine("After doing complicated work: {0}", i);
}
}`
You are sort of assuming because there is only one thread pumping messages in the UI that each item that is queued is processed in order. However, this is not true when methods put into the queue can themselves pump messages. In the example, the 3rd operation actually completes before the 2nd one. The DoComplicatedWork method is analogous to what is happening in SendWait.
I should answer your second question on how to prevent this. A lock won't help because they are re-entrant (i.e. the same thread can acquire a lock more than once). The best way is to disable the timer or detach the tick handler while inside the method and re-enable/attach the handler again before returning. You could also just try a simple boolean flag to indicate whether you are in the method already and return if so.
You can use the Reference Source or a good decompiler like Reflector or ILSpy to find out what's going on inside the framework code. The SendKeys.SendWait() method ends up calling a method on an internal class calls SKWindow to implement the "wait" request. That method looks like this:
public static void Flush()
{
Application.DoEvents();
while ((events != null) && (events.Count > 0))
{
Application.DoEvents();
}
}
DoEvents() is rather a notorious method, famous for crashing a lot of code. The re-entrancy you got on the timer's Tick event handler is fairly innocuous, this code can do a lot more damage to your program. You'll find the more common nastiness explained in this answer. Clearly you'll want to avoid SendWait() if you can.

Deadlock in WinForms that is prevented by right click on the taskbar

I encountered a strange problem with our Windows C# / .NET application. Actually it is a GUI application, my job is the included network component, encapsulated in an assembly. I do not know the code of the main/GUI application, I could contact it's developer though.
Now the application's UI has buttons to "Start" and "Stop" the network engine. Both buttons work.
To make my component threadsafe I am using a lock around three methods. I dont't want a client to be able to call Stop() before Start() finished. Additinally there is a Polling Timer.
I tried to show you as few lines as possible and simpified the problem:
private Timer actionTimer = new Timer(new
TimerCallback(actionTimer_TimerCallback),
null, Timeout.Infinite, Timeout.Infinite);
public void Start()
{
lock (driverLock)
{
active = true;
// Trigger the first timer event in 500ms
actionTimer.Change(500, Timeout.Infinite);
}
}
private void actionTimer_TimerCallback(object state)
{
lock (driverLock)
{
if (!active) return;
log.Debug("Before event");
StatusEvent(this, new StatusEventArgs()); // it hangs here
log.Debug("After event");
// Now restart timer
actionTimer.Change(500, Timeout.Infinite);
}
}
public void Stop()
{
lock (driverLock)
{
active = false;
}
}
Here is how to reproduce my problem. As I said, the Start and Stop buttons both work, but if you press Start(), and during the execution of the TimerCallback press Stop(), this prevents the TimerCallback to return. It hangs exactly at the same position, the StatusEvent. So the lock is never released and the GUI also hangs, because it's call of the Stop() method cannot proceed.
Now I observed the following: If the application hangs because of this "deadlock" and I click on the application in the task bar with the right mouse button, it continues. It just works as expected then. Anybody has an explanation or better a solution for this?
By the way, I also tried it with InvokeIfRequired as I don't know the internas of the GUI application. This is neccesary if my StatusEvent would change something in the GUI.
Since I have no reference to the GUI controls, I used (assuming only one target):
Delegate firstTarget = StatusEvent.GetInocationList()[0];
ISynchronizeInvoke syncInvoke = firstTarget.Target as ISynchronizeInvoke;
if (syncInvoke.InvokeRequired)
{
syncInvoke.Invoke(firstTarget, new object[] { this, new StatusEventArgs() });
}
else
{
firstTarget.Method.Invoke(firstTarget.Target, new object[] { this, new StatusEventArgs() });
}
This approach didn't change the problem. I think this is because I am Invoking on the main application's event handlers, not on the GUI controls. So the main app is responsible for Invoking? But anyway, AFAIK not using Invoke although needed would not result in a deadlock like this but (hopefully) in an exception.
As for why right-click "unlocks" your application, my "educated guess" of events that lead to this behaviour is as follows:
(when your component was created) GUI registered a subscriber to the status notification event
Your component acquires lock (in a worker thread, not GUI thread), then fires status notification event
The GUI callback for status notification event is called and it starts updating GUI; the updates are causing events to be sent to the event loop
While the update is going on, "Start" button gets clicked
Win32 sends a click message to the GUI thread and tries to handle it synchronously
Handler for the "Start" button gets called, it then calls "Start" method on your component (on GUI thread)
Note that the status update has not finished yet; start button handler "cut in front of"
the remaining GUI updates in status update (this actually happens quite a bit in Win32)
"Start" method tries to acquire your component's lock (on GUI thread), blocks
GUI thread is now hung (waits for start handler to finish; start handler waits for lock; the lock is held by worker thread that marshalled a GUI update call to GUI thread and waits for the update call to finish; the GUI update call marshalled from worker thread is waiting for start handler that cut in front of it to finish; ...)
If you now right-click on taskbar, my guess is that taskbar manager (somehow) starts a "sub-event-loop" (much like modal dialogs start their own "sub-event-loops", see Raymond Chen's blog for details) and processes queued events for the application
The extra event loop triggered by the right-click can now process the GUI updates that were marshalled from the worker thread; this unblocks the worker thread; this in turn releases the lock; this in turn unblocks application's GUI thread so it can finish handling start button click (because it can now acquire the lock)
You could test this theory by causing your application to "bite", then breaking into debugger and looking at the stack trace of the worker thread for your component. It should be blocked in some transition to GUI thread. The GUI thread itself should be blocked in the lock statement, but down the stack you should be able to see some "cut in front of the line" calls...
I think the first recommendation to be able to track this issue down would be to turn on the flag Control.CheckForIllegalCrossThreadCalls = true;.
Next, I would recommend firing the notification event outside of the lock. What I usually do is gather information needed by an event inside a lock, then release the lock and use the information I gathered to fire the event. Something along the lines:
string status;
lock (driverLock) {
if (!active) { return; }
status = ...
actionTimer.Change(500, Timeout.Infinite);
}
StatusEvent(this, new StatusEventArgs(status));
But most importantly, I would review who are the intended clients of your component. From the method names and your description I suspect GUI is the only one (it tells you when to start and stop; you tell it when your status changes). In that case you should not be using a lock. Start & stop methods could simply be setting and resetting a manual-reset event to indicate whether your component is active (a semaphore, really).
[update]
In trying to reproduce your scenario I wrote the following simple program. You should be able to copy the code, compile and run it without problems (I built it as a console application that starts a form :-) )
using System;
using System.Threading;
using System.Windows.Forms;
using Timer=System.Threading.Timer;
namespace LockTest
{
public static class Program
{
// Used by component's notification event
private sealed class MyEventArgs : EventArgs
{
public string NotificationText { get; set; }
}
// Simple component implementation; fires notification event 500 msecs after previous notification event finished
private sealed class MyComponent
{
public MyComponent()
{
this._timer = new Timer(this.Notify, null, -1, -1); // not started yet
}
public void Start()
{
lock (this._lock)
{
if (!this._active)
{
this._active = true;
this._timer.Change(TimeSpan.FromMilliseconds(500d), TimeSpan.FromMilliseconds(-1d));
}
}
}
public void Stop()
{
lock (this._lock)
{
this._active = false;
}
}
public event EventHandler<MyEventArgs> Notification;
private void Notify(object ignore) // this will be invoked invoked in the context of a threadpool worker thread
{
lock (this._lock)
{
if (!this._active) { return; }
var notification = this.Notification; // make a local copy
if (notification != null)
{
notification(this, new MyEventArgs { NotificationText = "Now is " + DateTime.Now.ToString("o") });
}
this._timer.Change(TimeSpan.FromMilliseconds(500d), TimeSpan.FromMilliseconds(-1d)); // rinse and repeat
}
}
private bool _active;
private readonly object _lock = new object();
private readonly Timer _timer;
}
// Simple form to excercise our component
private sealed class MyForm : Form
{
public MyForm()
{
this.Text = "UI Lock Demo";
this.AutoSize = true;
this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
var container = new FlowLayoutPanel { FlowDirection = FlowDirection.TopDown, Dock = DockStyle.Fill, AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink };
this.Controls.Add(container);
this._status = new Label { Width = 300, Text = "Ready, press Start" };
container.Controls.Add(this._status);
this._component.Notification += this.UpdateStatus;
var button = new Button { Text = "Start" };
button.Click += (sender, args) => this._component.Start();
container.Controls.Add(button);
button = new Button { Text = "Stop" };
button.Click += (sender, args) => this._component.Stop();
container.Controls.Add(button);
}
private void UpdateStatus(object sender, MyEventArgs args)
{
if (this.InvokeRequired)
{
Thread.Sleep(2000);
this.Invoke(new EventHandler<MyEventArgs>(this.UpdateStatus), sender, args);
}
else
{
this._status.Text = args.NotificationText;
}
}
private readonly Label _status;
private readonly MyComponent _component = new MyComponent();
}
// Program entry point, runs event loop for the form that excercises out component
public static void Main(string[] args)
{
Control.CheckForIllegalCrossThreadCalls = true;
Application.EnableVisualStyles();
using (var form = new MyForm())
{
Application.Run(form);
}
}
}
}
As you can see, the code has 3 parts - first, the component that is using timer to call notification method every 500 milliseconds; second, a simple form with label and start/stop buttons; and finally main function to run the even loop.
You can deadlock the application by clicking start button and then within 2 seconds clicking stop button. However, the application is not "unfrozen" when I right-click on taskbar, sigh.
When I break into the deadlocked application, this is what I see when switched to the worker (timer) thread:
And this is what I see when switched to the main thread:
I would appreciate if you could try compiling and running this example; if it works the same for you as me, you could try updating the code to be more similar to what you have in your application and perhaps we can reproduce your exact issue. Once we reproduce it in a test application like this, it shouldn't be a problem to refactor it to make the problem go away (we would isolate essence of the problem).
[update 2]
I guess we agree that we can't easily reproduce your behaviour with the example I provided. I'm still pretty sure the deadlock in your scenario is broken by an extra even loop being introduced on right-click and this event loop processing messages pending from the notification callback. However, how this is achieved is beyond me.
That said I would like to make the following recommendation. Could you try these changes in your application and let me know if they solved the deadlock problem? Essentially, you would move ALL component code to worker threads (i.e. nothing that has to do with your component will be running on GUI thread any more except code to delegate to worker threads :-) )...
public void Start()
{
ThreadPool.QueueUserWorkItem(delegate // added
{
lock (this._lock)
{
if (!this._active)
{
this._active = true;
this._timer.Change(TimeSpan.FromMilliseconds(500d), TimeSpan.FromMilliseconds(-1d));
}
}
});
}
public void Stop()
{
ThreadPool.QueueUserWorkItem(delegate // added
{
lock (this._lock)
{
this._active = false;
}
});
}
I moved body of Start and Stop methods into a thread-pool worker thread (much like your timers call your callback regularly in context of a thread-pool worker). This means GUI thread will never own the lock, the lock will only be acquired in context of (probably different for each call) thread-pool worker threads.
Note that with the change above, my sample program doesn't deadlock any more (even with "Invoke" instead of "BeginInvoke").
[update 3]
As per your comment, queueing Start method is not acceptable because it needs to indicate whether the component was able to start. In this case I would recommend treating the "active" flag differently. You would switch to "int" (0 stopped, 1 running)and use "Interlocked" static methods to manipulate it (I assume that your component has more state it exposes - you would guard access to anything other than "active" flag with your lock):
public bool Start()
{
if (0 == Interlocked.CompareExchange(ref this._active, 0, 0)) // will evaluate to true if we're not started; this is a variation on the double-checked locking pattern, without the problems associated with lack of memory barriers (see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html)
{
lock (this._lock) // serialize all Start calls that are invoked on an un-started component from different threads
{
if (this._active == 0) // make sure only the first Start call gets through to actual start, 2nd part of double-checked locking pattern
{
// run component startup
this._timer.Change(TimeSpan.FromMilliseconds(500d), TimeSpan.FromMilliseconds(-1d));
Interlocked.Exchange(ref this._active, 1); // now mark the component as successfully started
}
}
}
return true;
}
public void Stop()
{
Interlocked.Exchange(ref this._active, 0);
}
private void Notify(object ignore) // this will be invoked invoked in the context of a threadpool worker thread
{
if (0 != Interlocked.CompareExchange(ref this._active, 0, 0)) // only handle the timer event in started components (notice the pattern is the same as in Start method except for the return value comparison)
{
lock (this._lock) // protect internal state
{
if (this._active != 0)
{
var notification = this.Notification; // make a local copy
if (notification != null)
{
notification(this, new MyEventArgs { NotificationText = "Now is " + DateTime.Now.ToString("o") });
}
this._timer.Change(TimeSpan.FromMilliseconds(500d), TimeSpan.FromMilliseconds(-1d)); // rinse and repeat
}
}
}
}
private int _active;
A couple things come to mind when reviewing your code. The first thing is that you are not checking for a null delegate before firing the status event. If no listeners are bound to the event, then this will cause an exception, which if not caught or handled, might cause strange issues in threaded code.
So the first thing I'd so is this:
if(StatusEvent != null)
{
StatusEvent(this, new StatusEventArgs());
}
The other thing that comes to mind is that perhaps your lock is failing you in some manner. What type of object are you using for the lock? The simplest thing to use is just a plain ole "object", but you must ensure you are not using a value type (e.g. int, float, etc.) that would be boxed for locking, thus never really establishing a lock since each lock statement would box and create a new object instance. You should also keep in mind that a lock only keeps "other" threads out. If called on the same thread, then it will sail through the lock statement.
If you don't have the source for the GUI (which you probably should) you can use Reflector to disassemble it. There is even a plugin to generate source files so you could run the app in your VS IDE and set breakpoints.
Not having access to the GUI source makes this harder, but a general tip here... The WinForm GUI is not managed code, and doesn't mix well with .NET threading. The recommended solution for this is to use a BackgroundWorker to spawn a thread that is independent of the WinForm. Once you're running in the thread started by the BackgroundWorker, you're in pure managed code and you can use .NET's timers and threading for pretty much anything. The restriction is that you have to use the BackgroundWorker's events to pass information back to the GUI, and your thread started by the BackgroundWorker can't access the Winform controls.
Also, you'd be well off to disable the "Stop" button while the "Start" task is running, and vice versa. But a BackgroundWorker is still the way to go; that way the WinForm doesn't hang while the background thread is running.
Yes, this is a classic deadlock scenario. The StatusEvent cannot proceed because it needs the UI thread to update the controls. The UI thread is however stuck, trying to acquire the driverLock. Held by the code that calls StatusEvent. Neither thread can proceed.
Two ways to break the lock:
the StatusEvent code might not necessarily need to run synchronously. Use BeginInvoke instead of Invoke.
the UI thread might not necessarily need to wait for the thread to stop. Your thread could notify it later.
There is not enough context in your snippets to decide which one is better.
Note that you might have a potential race on the timer too, it isn't visible in your snippet. But the callback might run a microsecond after the timer was stopped. Avoid this kind of headache by using a real thread instead of a timer callback. It can do things periodically by calling WaitOne() on a ManualResetEvent, passing a timeout value. That ManualResetEvent is good to signal the thread to stop.
A wild guess here: Could the status message somehow be causing the other app to call your Stop task?
I would put debug stuff at the start of all three methods, see if you're deadlocking on yourself.

Correct way to have an endless wait

So I have a program that has a list of timers. Each of the timers has a tick event and lets just say for example, i have 10 timers started (all are in the List).
What is the best way to sit forever (or until i tell it to stop)? Should I just have a while loop?
foreach(Timer t in _timers)
{
t.Start();
}
while(true)
{
Application.DoEvents();
System.Threading.Thread.Sleep(5000);
}
I have a feeling that this isn't the best way...
-- Update
Here's my entire program:
public static void Main()
{
// set some properties and set up the timers
foreach(Timer t in _timers)
{
t.Start();
}
while(true)
{
Application.DoEvents();
System.Threading.Thread.Sleep(5000);
}
}
Thats it. There is no UI, there's nothing else. If I don't have the while loop, then the program just finishes.
Use an EventWaitHandle or array of EventWaitHandles to block thread execution by using the WaitOne() or WaitAll() methods.
http://msdn.microsoft.com/en-us/library/kad9xah9.aspx
So for example
ManualResetEvent mre = new ManualResetEvent(false);
mre.WaitOne();
will wait for eternity.
edit
Since you're making a service, you might want to read this article.
By the Application.DoEvents, I assume you are on a UI thread here. It is never a good idea to keep the UI thread active (even with DoEvents). Why not just start the timers and release control back to the message pump. When the events tick it'll pick up the events.
Why do you want to loop?
Re the update; which Timer are you using? If you use System.Timers.Timer (with the Elapsed event) then it isn't bound to the message-loop (it fires on a separate thread): you can just hang the main thread, perhaps waiting on some exit condition:
using System;
using System.Timers;
static class Program {
static void Main() {
using (Timer timer = new Timer()) {
timer.Interval = 2000;
timer.Elapsed += delegate {
Console.Error.WriteLine("tick");
};
timer.Start();
Console.WriteLine("Press [ret] to exit");
Console.ReadLine();
timer.Stop();
}
}
}
You could wait on a condition variable, or select() on a socket.
Depending on how you're exiting the program, you might consider using only nine timers and have the tenth activity part of the main thread of your code.
Each of those timers is a separate thread and should be handled carefully.
DoEvents is considered 'evil' and should be avoided. http://msdn.microsoft.com/en-us/library/system.windows.forms.application.doevents.aspx

Categories