天天看點

第二十章:異步和檔案I/O.(一)

圖形使用者界面具有一點點特性,具有深遠的影響:必須按順序處理使用者對應用程式的輸入。無論使用者輸入事件是來自鍵盤,滑鼠還是觸摸,每個事件必須由應用程式完全處理 - 直接或通過使用者界面對象(如按鈕或滑塊) - 在應用程式獲得下一個使用者之前 - 來自作業系統的輸入事件。

經過一些反思後,這個限制背後的基本原理變得清晰,可能是一個例子:假設一個頁面包含兩個按鈕,使用者可以快速點選一個然後另一個按鈕。這兩個按鈕可能會在兩個獨立的執行線程中同時處理這兩個分接頭嗎?不,那不行。可能是第一個按鈕改變了第二個按鈕的含義,可能完全禁用它。是以,在第二個按鈕開始處理自己的點選之前,必須允許第一個按鈕完全處理其點選。

此限制的後果非常嚴重:必須在單個執行線程中處理對特定應用程式的所有使用者輸入。而且,使用者界面對象通常不是線程安全的。無法從輔助執行線程修改它們。是以,與應用程式的使用者界面連接配接的所有代碼僅限于一個線程。此線程稱為主線程或使用者界面線程或UI線程。

幾十年來,随着使用者越來越習慣于圖形使用者界面,我們越來越不能容忍即使是最輕微的響應性失誤。作為應用程式員,我們是以盡力保持使用者界面響應以實作最大的使用者滿意度。這意味着在UI線程上運作的任何内容都必須盡快執行其處理并将控制權傳回給作業系統。如果在UI線程中運作的事件處理程式在長處理作業中陷入困境,整個使用者界面似乎會當機并且肯定會使使用者煩惱。

是以,應用程式必須執行的任何冗長的作業都應該降級為執行的輔助線程,通常稱為工作線程。據說這些工作線程“在背景運作”并且不會幹擾UI線程的響應性。

你已經在本書中看到了一些例子。幾個示例程式 - 第13章中的ImageBrowser和BitmapStreams程式,“位圖”,以及第19章“集合視圖”中的SchoolOfFineArt庫和RssFeed程式 - 使用WebRequest類通過Internet下載下傳檔案。對WebRequest的BeginGetResponse方法的調用啟動了一個異步通路Web資源的工作線程。 WebRequest調用快速傳回,程式可以在下載下傳檔案時處理其他使用者輸入。 BeginGetResponse的參數是在背景程序完成時調用的回調方法。在此回調方法中,程式調用EndGetResponse來通路下載下傳的資料。

但傳遞給BeginGetResponse的回調方法有一點問題。回調方法在下載下傳檔案的同一工作線程中運作,在一般情況下,您無法從UI線程以外的任何其他方式通路使用者界面對象。通常,這意味着回調方法必須通路UI線程。 Xamarin.Forms支援的三個平台中的每一個都有自己的本機方法,用于從UI線程上的輔助線程運作代碼,但在Xamarin.Forms中,這些都可以通過Device.BeginInvokeOnMainThread方法獲得。 (但是,您會記得,通常有一些與ViewModel相關的異常:雖然輔助線程無法直接通路使用者界面對象,但輔助線程可以設定通過使用者界面對象綁定的屬性資料綁定。)

近年來,異步處理在程式員變得更容易的同時變得越來越普遍。這是一個持續的趨勢:計算的未來無疑将涉及更多的異步計算和并行處理,特别是随着多核處理器晶片的使用越來越多。開發人員需要良好的作業系統支援和語言工具來處理異步操作,幸運的是.NET和C#一直處于這種支援的最前沿。

本章将探讨在Xamarin.Forms應用程式中使用異步處理的一些基礎知識,包括使用.NET Task類來幫助您定義和使用異步方法。使用C#5.0中引入的兩個關鍵字:async和await,大大減輕了處理回調函數的習慣麻煩。 await運算符通過簡化異步調用的文法,澄清異步調用的程式流,通過簡化使用者界面對象的通路,簡化工作線程引發的異常處理,以及統一處理異步調用,徹底改變了異步程式設計的文法。這些例外和取消背景工作。

本章主要示範如何使用異步處理來執行檔案輸入和輸出,以及如何建立自己的工作線程以執行冗長的作業。

但是Xamarin.Forms本身包含幾種異步方法。

繼續閱讀