please wait for the next slide clicking won’t make it come any faster
Click Sub LoadSettings() IO.File.ReadAllText(path) End Sub Sub Button1_Click(...) LoadSettings() UpdateView() End Sub Click Message pump
Click Sub LoadSettings() IO.Network.Download(path) End Sub Sub Button1_Click(...) LoadSettings() UpdateView() End Sub Click Message pump
Demo Add the Async & Await keywords
Async Sub Button1_Click(..) Await LoadSettingsAsync() UpdateView() End Sub Async Function LoadSettingsAsync() As Task Await IO.Network.DownloadAsync(path) End Sub Message pump Sub LoadSettings() IO.Network.Download(path) End Sub Sub Button1_Click(..) LoadSettings() UpdateView() End Sub Click
Async Function LoadSettingsAsync() As Task Await IO.Network.DownloadAsync(path) End Sub Async Sub Button1_Click(...) Await LoadSettingsAsync() UpdateView() End Sub Click Message pump Task... DownloadAsync Task... LoadSettingsAsync Download LoadSettings
Demo Async UI app: re-entrancy and deadlock Async unit test Async console app
Photo: Rüdiger Wölk
Demo Using the Task type for compositional algorithms Cancellation
This is the IL that’s emitted when you use async/await
Async Function LoadSettingsAsync() As Task(Of Integer) Button1.IsEnabled = False Dim downTask = IO.Network.DownloadAsync(path) Me.Config = Await downTask Return Me.Config.Length End Function Async Sub Button1_Click() Dim loadTask = LoadSettingsAsync() Dim len = Await loadTask UpdateView() End Sub UI thread IOCP thread
Async Sub Button1_Click() Dim loadTask = LoadSettingsAsync() Dim len = Await loadTask UpdateView() End Sub Async Function LoadSettingsAsync() As Task(Of Integer) Button1.IsEnabled = False Dim downTask = IO.Network.DownloadAsync(path) Me.Config = Await downTask Return Me.Config.Length End Function UI thread IOCP thread Click [1/12] A button-click arrives on the UI queue
Async Sub Button1_Click() Dim loadTask = LoadSettingsAsync() Dim len = Await loadTask UpdateView() End Sub Async Function LoadSettingsAsync() As Task(Of Integer) Button1.IsEnabled = False Dim downTask = IO.Network.DownloadAsync(path) Me.Config = Await downTask Return Me.Config.Length End Function UI thread IOCP thread Click downTask [2/12] Invoke some functions; get back “downTask” from the API
Async Function LoadSettingsAsync() As Task(Of Integer) Button1.IsEnabled = False Dim downTask = IO.Network.DownloadAsync(path) Me.Config = Await downTask Return Me.Config.Length End Function downTask Async Sub Button1_Click() Dim loadTask = LoadSettingsAsync() Dim len = Await loadTask UpdateView() End Sub UI thread IOCP thread Click downTask » sc.Post{Κ1} [3/12] “Await downTask” assigns a continuation and returns loadTask loadTask Κ1:
Async Sub Button1_Click() Dim loadTask = LoadSettingsAsync() Dim len = Await loadTask UpdateView() End Sub loadTask Async Function LoadSettingsAsync() As Task(Of Integer) Button1.IsEnabled = False Dim downTask = IO.Network.DownloadAsync(path) Me.Config = Await downTask Return Me.Config.Length End Function UI thread IOCP thread Click downTask » sc.Post{Κ1} [4/12] “Await loadTask” assigns a continuation and returns Κ2: loadTask » sc.Post{Κ2} Κ1:
Async Sub Button1_Click() Dim loadTask = LoadSettingsAsync() Dim len = Await loadTask UpdateView() End Sub Async Function LoadSettingsAsync() As Task(Of Integer) Button1.IsEnabled = False Dim downTask = IO.Network.DownloadAsync(path) Me.Config = Await downTask Return Me.Config.Length End Function UI thread IOCP thread Click conf [5/12] Network packet arrives with data downTask » sc.Post{Κ1} Κ2: loadTask » sc.Post{Κ2} Κ1:
Async Sub Button1_Click() Dim loadTask = LoadSettingsAsync() Dim len = Await loadTask UpdateView() End Sub Async Function LoadSettingsAsync() As Task(Of Integer) Button1.IsEnabled = False Dim downTask = IO.Network.DownloadAsync(path) Me.Config = Await downTask Return Me.Config.Length End Function UI thread IOCP thread Click conf sc.Post{Κ1(conf)} [6/12] Invoke downTask’s continuation with that data downTask » sc.Post{Κ1} Κ2: loadTask » sc.Post{Κ2} Κ1:
Async Sub Button1_Click() Dim loadTask = LoadSettingsAsync() Dim len = Await loadTask UpdateView() End Sub Async Function LoadSettingsAsync() As Task(Of Integer) Button1.IsEnabled = False Dim downTask = IO.Network.DownloadAsync(path) Me.Config = Await downTask Return Me.Config.Length End Function UI thread IOCP thread Click loadTask » sc.Post{Κ2} conf K1(conf) [7/12] Continuation is a “Post”, i.e. addition to the UI queue Κ2: sc.Post{Κ1(conf)} Κ1:
Async Sub Button1_Click() Dim loadTask = LoadSettingsAsync() Dim len = Await loadTask UpdateView() End Sub Async Function LoadSettingsAsync() As Task(Of Integer) Button1.IsEnabled = False Dim downTask = IO.Network.DownloadAsync(path) Me.Config = Await downTask Return Me.Config.Length End Function UI thread IOCP thread Click conf K1(conf) [8/12] UI thread executes K1 Κ2: sc.Post{Κ1(conf)} loadTask » sc.Post{Κ2} Κ1:
Async Sub Button1_Click() Dim loadTask = LoadSettingsAsync() Dim len = Await loadTask UpdateView() End Sub Async Function LoadSettingsAsync() As Task(Of Integer) Button1.IsEnabled = False Dim downTask = IO.Network.DownloadAsync(path) Me.Config = Await downTask Return Me.Config.Length End Function UI thread IOCP thread Click conf K1(conf) [9/12] Return statement will signal completion of loadTask Κ2: sc.Post{Κ1(conf)} loadTask » sc.Post{Κ2} Κ1:
Async Sub Button1_Click() Dim loadTask = LoadSettingsAsync() Dim len = Await loadTask UpdateView() End Sub Async Function LoadSettingsAsync() As Task(Of Integer) Button1.IsEnabled = False Dim downTask = IO.Network.DownloadAsync(path) Me.Config = Await downTask Return Me.Config.Length End Function UI thread IOCP thread Click conf K1(conf) sc.Post(Κ2(len)) [10/12] Invoke loadTask’s continuation with data (by posting to UI queue) K2(len) Κ2: sc.Post{Κ1(rss)} loadTask » sc.Post{Κ2} Κ1:
Async Sub Button1_Click() Dim loadTask = LoadSettingsAsync() Dim len = Await loadTask UpdateView() End Sub Async Function LoadSettingsAsync() As Task(Of Integer) Button1.IsEnabled = False Dim downTask = IO.Network.DownloadAsync(path) Me.Config = Await downTask Return Me.Config.Length End Function UI thread IOCP thread Click conf K1(rss) sc.Post(Κ2(len)) K2(len) [11/12] Return from handling the K1 continuation Κ2: sc.Post{Κ1(conf)} Κ1:
Async Function LoadSettingsAsync() As Task(Of Integer) Button1.IsEnabled = False Dim downTask = IO.Network.DownloadAsync(path) Me.Config = Await downTask Return Me.Config.Length End Function Async Sub Button1_Click() Dim loadTask = LoadSettingsAsync() Dim len = Await loadTask UpdateView() End Sub conf K2(len) Click K1(rss) UI thread IOCP thread sc.Post(Κ2(len)) Κ1: Κ2: [12/12] UI thread executes K2 sc.Post{Κ1(conf)}
“A waiter’s job is to wait on a table until the patrons have finished their meal. If you want to serve two tables concurrently, you must hire two waiters.”
The following is from the Android dev blog. Can you spot the flaw? 1. “A good practice in creating responsive applications is to make sure your main UI thread does the minimum amount of work.” 2. “Any potentially long task that may hang your application should be handled in a different thread.” 3. “Typical examples of such tasks are network operations, which involve unpredictable delays.”