天天看點

第十章(1)-異步程式設計模式-學習筆記

完成各種各樣的資訊處理工作室人們開發應用軟體最重要的目的之一。有些資訊處理工作需要較長的時間來完成,比如在包含很多檔案的硬碟上查找特定的檔案,在這種情況下讓應用程式停下來等待這一項工作完成是不現實的。應該讓其在背景運作,這是應用程式還可以被使用者操作,這需要異步程式設計的知識。

.NET的異步程式設計技術主要分為:

1,使用IAsyncResult的異步程式設計模式:又分為基于委托和世界是用基類庫中的相關元件兩種類型;

2,基于事件的異步程式設計(EAP)。

開發一個EAP元件相當繁瑣,實際中并不常用,是以主要學習上面的第一種模式程式設計和了解如何使用現有的EAP元件。

程式的同步執行和異步執行

在許多程式中代碼時順序執行的,如果在代碼中調用了一個方法,則必須等待此方法的所有代碼均執行完畢之後,才能回到原來的地方執行下一行代碼。這種程式運作的方式稱為“同步”;

簡單例子檢視檔案容量:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace CalculateFolderSizeNoAsync
{
    class Program
    {
        //計算指定檔案夾的總容量
        private static long CalculateFolderSize(string FolderName)
        {
            if (Directory.Exists(FolderName) == false)
            {
                throw new DirectoryNotFoundException("檔案夾不存在");
            }

            DirectoryInfo RootDir = new DirectoryInfo(FolderName);
            //擷取所有的子檔案夾
            DirectoryInfo[] ChildDirs = RootDir.GetDirectories();
            //擷取目前檔案夾中的所有檔案
            FileInfo[] files = RootDir.GetFiles();
            long totalSize = 0;
            //累加每個檔案的大小
            foreach (FileInfo file in files)
            {
                totalSize += file.Length;
            }
            //對每個檔案夾執行同樣的計算過程:累加其下每個檔案的大小
            //這是通過遞歸調用實作的
            foreach (DirectoryInfo dir in ChildDirs)
            {
                totalSize += CalculateFolderSize(dir.FullName);
            }
            //傳回檔案夾的總容量
            return totalSize;
        }

        static void Main(string[] args)
        {
            long size;
            string FolderName;


            Console.WriteLine("請輸入檔案夾名稱(例如:C:\\Windows):");

            FolderName = Console.ReadLine();


            size = CalculateFolderSize(FolderName);

            Console.WriteLine("\n檔案夾{0}的容量為:{1}位元組\n", FolderName, size);

            Console.ReadKey();

        }
    }
}
           

對于層次很深檔案數量很大的檔案,需要很長時間,才能檢索完畢。在這段時間内,應用程式隻能等待(使用者有可能感覺程式死了,沒有任何提示)下面應用異步方法會有不一樣的體驗。

将上述程式改為異步執行:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace CalculateFolderSizeNoAsync
{
    class Program
    {
        //計算指定檔案夾的總容量
        private static long CalculateFolderSize(string FolderName)
        {
            if (Directory.Exists(FolderName) == false)
            {
                throw new DirectoryNotFoundException("檔案夾不存在");
            }

            DirectoryInfo RootDir = new DirectoryInfo(FolderName);
            //擷取所有的子檔案夾
            DirectoryInfo[] ChildDirs = RootDir.GetDirectories();
            //擷取目前檔案夾中的所有檔案
            FileInfo[] files = RootDir.GetFiles();
            long totalSize = 0;
            //累加每個檔案的大小
            foreach (FileInfo file in files)
            {
                totalSize += file.Length;
            }
            //對每個檔案夾執行同樣的計算過程:累加其下每個檔案的大小
            //這是通過遞歸調用實作的
            foreach (DirectoryInfo dir in ChildDirs)
            {
                totalSize += CalculateFolderSize(dir.FullName);
            }
            //傳回檔案夾的總容量
            return totalSize;
        }
        public delegate long CalculateFolderSizeD(string FolderName);
        static void Main(string[] args)
        {
            long size;
            string FolderName;
            CalculateFolderSizeD d = CalculateFolderSize; 
            Console.WriteLine("請輸入檔案夾名稱(例如:C:\\Windows):");
            FolderName = Console.ReadLine();
            //通過委托異步調用靜态方法CalculateFolderSize
            IAsyncResult ret = d.BeginInvoke(FolderName, null, null);
            Console.WriteLine("正在計算中,請耐心等待……");
            //阻塞,等到調用完成,取出結果
            size = d.EndInvoke(ret);
            //size = CalculateFolderSize(FolderName);//不用這個了
            Console.WriteLine("\n檔案夾{0}的容量為:{1}位元組\n", FolderName, size);
            Console.ReadKey();

        }
    }
}
           

異步程式設計的基礎是委托與多線程,上面示例中定義了一個委托,編譯器在編譯上述委托定義語句是,會自動産生一下類的模闆:

public sealed class CalculateFolderSizeD : MulticastDelegate
        {
            public CalculateFolderSizeD (Object target,int methodPtr)
            { .... }
            public virtual long Inoke (string FolderName)
            { .... }
            public virtual IAsyncResult BeginInvoke(string FolderName, AsyncCallback callback, object asyncState)
            { .... }
            public virtual long EndInvoke(IAsyncResult result)
            { .... }
        }
           

當使用同步模式直接運用位委托調用方法時:

<span style="white-space:pre">	</span>CalculateFolderSizeD d = CalculateFolderSize;
<span style="white-space:pre">	</span>d(FolderName);
           

編譯器會調用轉換為相應的Invoke方法,一步是相反。BeginInvoke方法參數的第一部分是定義委托是确定的方法簽名中的參數清單,第二個參數callback是當異步調用結束時自動回調的方法,第三個參數asyncSate用于向第二個參數所确定的callback回調方法提供額外的資訊。

注意:可以檢視一下IAsyncResult接口的定義。(對了解内容有所幫助)

#region 程式集 mscorlib.dll, v4.0.0.0
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1\mscorlib.dll
#endregion

using System.Runtime.InteropServices;
using System.Threading;

namespace System
{
    // 摘要: 
    //     表示異步操作的狀态。
    [ComVisible(true)]
    public interface IAsyncResult
    {
        // 摘要: 
        //     擷取使用者定義的對象,它限定或包含關于異步操作的資訊。
        //
        // 傳回結果: 
        //     使用者定義的對象,它限定或包含關于異步操作的資訊。
        object AsyncState { get; }
        //
        // 摘要: 
        //     擷取用于等待異步操作完成的 System.Threading.WaitHandle。
        //
        // 傳回結果: 
        //     用于等待異步操作完成的 System.Threading.WaitHandle。
        WaitHandle AsyncWaitHandle { get; }
        //
        // 摘要: 
        //     擷取一個值,該值訓示異步操作是否同步完成。
        //
        // 傳回結果: 
        //     如果異步操作同步完成,則為 true;否則為 false。
        bool CompletedSynchronously { get; }
        //
        // 摘要: 
        //     擷取一個值,該值訓示異步操作是否已完成。
        //
        // 傳回結果: 
        //     如果操作完成則為 true,否則為 false。
        bool IsCompleted { get; }
    }
}
           

下一節會用到這些内容!

繼續閱讀