Async, Await and the UI Problem |
Written by Mike James | ||||
Thursday, 25 February 2021 | ||||
Page 3 of 3
DoEvents and dangersIf you have used the DoEvents command in Visual Basic you might see that this is a very similar mechanism in that it allows the UI thread to process the Dispatch queue but also notice that this is more sophisticated in that another thread is used to do the work. Just as there were dangers in using DoEvents and similar constructs there are even more dangers in using async/await. For example, as the UI stays responsive, the user can click the same button more than once and create multiple tasks. Usually this is not a good idea and so the event handler usually disables the button while the task is running: private async void button1_Click( If you try writing this simple idea using threads and callbacks you will see just how neat the async/await pattern is. Multiple awaitsThis example just gets us started because it raises a few questions of its own. In particular, we still have the problem of allowing the worker code to update the UI. The key factor is that the event handler can update the UI each time it is restarted so we could use an approach where the work is broken into chunks which return to the event handler for an update. For example: private async void button1_Click( In this case what happens is that DoWork runs for 5 seconds and then summons the UI thread back to continue the event handler from where it left off. This results in the text "First work done" being displayed. Then the second await lets the UI thread get on with processing the Dispatcher queue, only to be called back to continue from where it left off 5 seconds later. Hence we see the second work done update message. OK, so you could break work down into chunks like this and even put the chunks into a for loop, say, to automate the process: private async void button1_Click( However if all you want to do is report the progress of a longer computation via the UI then there is an alternative way of doing the job by passing in a method that can be called on the UI thread by the worker. Getting resultsAn even more important issue, however, is how the worker thread can return a result. For simplicity all of the examples so far have returned void, but in real life the worker usually returns some value, usually a complex object. Getting result back turns out to be remarkably easy. To return a type t say we simply change the Task to Task<t> and return a t at the end of the code run on the worker thread. For example to return an int from DoWork you would write: Task<int> DoWork() where the code run on the new thread returns 500 just to show that it works. In the asychronous method we simply call the new DoWork in the obvious way: int result=await DoWork(); Notice that there are a few points of potential confusion. The return type of DoWork is stated as Task<int> yet it actually seems to return an <int> to the calling routine. Also there now appear to be two returns in DoWork, the one that returns the Task<int> and the one that returns the int. If you have followed how all of this works then this should seem understandable if potentially confusing. This article has explained how the new async/await facilities can make writing good UI code easy but the same ideas apply more generally and you can write asynchronous methods that can be called from any thread, not just the UI thread. There are also lots of other facilities - a cancellation mechanism, a progress reporting mechanism and so on - that make the whole even more flexible.
Deep C#Buy Now From Amazon Chapter List
Extra Material <ASIN:1871962714> <ASIN:B09FTLPTP9>
Comments
or email your comment to: comments@i-programmer.info To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin. <ASIN:0596002521> <ASIN:0321658701> <ASIN:1118162137> <ASIN:0321877586>
|
||||
Last Updated ( Thursday, 25 February 2021 ) |