| What Is Asynchronous Programming? |
| Written by Mike James | ||||
| Friday, 04 March 2022 | ||||
Page 2 of 3
Now we come back to the problem of the single threading. If any event handler takes a long time or worse never returns then the UI seems to freeze. The reason is simply that if the event handler never returns the thread to the dispatcher and hence no other event handler ever gets to run. If the event handler holds the thread for any noticeable length of time then events are not processed and the user thinks that the UI is unresponsive for that time. The cure for this problem is to keep the work done by any event handler to a minimum. In a sense the event handling system is a cooperative multitasker and it in this case it is the duty of every event handler to return control to the dispatcher as soon as possible. The fair sharing of the thread of execution is a matter of cooperation between all of the code that needs to run i.e. the event handlers. So what do you do if an event handler has a substantial amount of work to do? The textbook answer is that you simply use the event handler to set up another thread and get the new thread to do all the work. The event handler returns almost at once and the new thread continues to do the job. This use of a new thread causes problems of synchronization that we will return to a little later. In short adding threads to a single threaded event system makes it much more complex. Hence the non-textbook non-approved way of dealing with long running event handlers - doEvents. Some languages have a special doEvents command which acts like a yield command - in that it transfers control to the calling routine, the dispatcher in this case, which then processes all of the pending events and when done transfers control back to the suspended event handler at the instruction following the doEvents. The state of the event handler is unchanged at it continues on as if nothing had happened. In other words the doEvents command does what it says it does - it pauses the event handler and allows the dispatcher to "do" any pending events so keeping the UI responsive. Languages that don't have a doEvents or similar can often implement the same thing if they provide access to the dispatcher from running event handlers. Basically the event handler puts an event record at the end of the queue that restarts it and then returns control to the dispatcher. This seems the ideal solution. A long running event handler cooperatively yields the thread of execution so that other events can be processed and the UI kept active. When the events have been processed then the long running event handler is resumed. It is indeed a good solution but it has a flaw that is so serious that many give the advice that it is to be avoided at all cost and its bad practice. The flaw is simply that the event handler that yields control is now no longer atomic. It is interrupted at some point in its code and other event handlers could change things that it is using. Worse it could even be called again as the result of an event in the queue. Most event handlers are not reenterable and things could get very complicated. However a programmer, aware of these difficulties can make doEvents work. You have to take account of the fact that shared resources could change after a doEvents and you have to reject any attempt to start the event handler again while is is still active. Both of these are usually easy to achieve and arguably much easier than starting a worker thread to get the same job done. However for beginners the rule "don't use doEvents" is a safe one. |
||||
| Last Updated ( Friday, 04 March 2022 ) |
