I have an object, Timeline, which encapsulates a stream. Events can be scheduled on a timeline; the thread will wait until an event is executed, execute it, and return to sleep (for (a) the time required to move to the next event, or (b) for an indefinite period if there are no more events).
Sleep is handled with a WaitEventHandle, which is triggered when the list of events changes (because sleep delay adjustments may be required) or when the thread needs to be stopped (so the thread can end gracefully).
The destructor calls Stop (), and I even implemented IDisposable and Dispose () also calls Stop ().
However, when I use this component in a forms application, my application will never be closed when I close the form. For some reason, Stop () is never called, so no object destructor is started, and the Dispose () method is not called before .NET decides to wait for all threads to finish.
I suppose the solution would be to explicitly call Dispose () directly in the FormClose event, but since this class will be in the library and this is actually a layer deeper (i.e. the application developer will never see the Timeline class), it seems very ugly and superfluous (unnecessary) for the application developer. The use () clause, which I usually use when releasing a resource, becomes a problem, does not apply, as it will be a long-lived object.
On the one hand, I can understand that .NET wants to wait for all threads to finish before it finishes the final garbage collection, but in this case it creates a very awkward situation.
How can I clear my stream after myself without adding requirements to the consumers of my library? In other words, how can I get .NET to notify my object when the application exits, but before it waits for all threads to complete?
EDIT: In response, people saying that the client program normally knows about the thread: I respectfully disagree.
As I said in my original post, the stream is hiding in another object (Animator). I am creating an Animator for another object, and I tell him to perform the animation, for example, "blink this light for 800 ms."
Animator, , Animator , 800 . ? . (ew)? . , ? .
, , - Animator, , , . , .
EDIT: , . , , :
internal class Timeline : IDisposable {
private Thread eventThread;
private volatile bool active;
private SortedList<DateTime, MethodInvoker> events = new SortedList<DateTime,MethodInvoker>();
private EventWaitHandle wakeup = new EventWaitHandle(false, EventResetMode.AutoReset);
internal Timeline() {
active = true;
eventThread = new Thread(executeEvents);
eventThread.Start();
}
~Timeline() {
Dispose();
}
private DateTime NextEvent {
get {
lock(events)
return events.Keys[0];
}
}
private void executeEvents() {
while (active) {
while (events.Count > 0 && NextEvent <= DateTime.Now) {
lock(events) {
events.Values[0]();
events.RemoveAt(0);
}
}
if (events.Count > 0)
wakeup.WaitOne((int)(NextEvent - DateTime.Now).TotalMilliseconds);
else
wakeup.WaitOne();
}
}
internal void Stop() {
active = false;
wakeup.Set();
}
public void Dispose() {
Stop();
}
}