C# 非同期 APM モードの非同期プログラム開発例の共有

黄舟
リリース: 2018-05-11 11:32:56
オリジナル
1922 人が閲覧しました

C# には 10 年以上の歴史があり、Microsoft の 2 年ごとのアップデートの進捗から判断すると、C# の非同期プログラミングも複数のバージョンの進化を経験しています。 C# における非同期プログラミングの開発履歴を記録する投稿です。 広告: 私の記事が気に入ったお友達は、下の「フォローする」をクリックしてください。ありがとうございます

私がC#に触れて使ったのは2004年です。当時のC#のバージョンは1.1だったので、その頃からお話します。当時、大学で自分でプログラムを読んだり書いたりしていたとき、私が書いたプログラムのほとんどは同期プログラムで、せいぜい 1 つのスレッドしか起動できませんでした。実際、C# 1.1 の時代には、完全なプログラムがありました。非同期プログラミング ソリューション、それが APM (非同期プログラミング モデル) でした。 「同期プログラムと非同期プログラム」がまだわからない場合は、Baidu にアクセスしてください。

APM 非同期プログラミング モデルの最も代表的な特徴は、非同期関数が Begin と End で始まる 2 つのメソッドで構成されることです。 Beginで始まるメソッドは非同期関数の実行を開始することを意味し、Endで始まるメソッドは非同期関数の実行が終了するのを待って実行結果を返すことを意味します。以下はシミュレーションの実装です (標準の APM モデルの非同期実装は後で記述します):

public class Worker
    {        
    public int A { get; set; }        
    public int B { get; set; }        
    private int R { get; set; }
        ManualResetEvent et;        
        public void BeginWork(Action action)
        {
            et = new ManualResetEvent(false);            
            new Thread(() =>
            {
                R = A + B;
                Thread.Sleep(1000);
                et.Set();                
                if(null != action)
                {
                    action();
                }
            }).Start();
        }        public int EndWork()
        {            if(null == et)
            {                t
            hrow new Exception("调用EndWork前,需要先调用BeginWork");
            }            
            else
            {
                et.WaitOne();                
                return R;
            }

        } 
    }
ログイン後にコピー
        static void Main(string[] args)
        {
           Worker w = new Worker();
            w.BeginWork(()=> {
                Console.WriteLine("Thread Id:{0},Count:{1}", Thread.CurrentThread.ManagedThreadId,
                    w.EndWork());
            });
            Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);
            Console.ReadLine();
        }
ログイン後にコピー

上記のシミュレーション APM モデルでは、Thread と ManualResetEvent を使用しました。マルチスレッドと ManualResetEvent に慣れていない場合は、非同期プログラミングを使用してください。 C# これには必然的にマルチスレッドの知識が必要になりますが、Microsoft は Framework で多くのカプセル化を行ってきましたが、その本質を理解する必要があります。

上記でシミュレートした APM 非同期モデルが単純である理由は、C# の開発プロセス中に多くの優れた文法規則が導入されたためです。上記の例では、ラムダ式をさらに使用しました。匿名デリゲートとラムダ式に詳しくない場合は、以前の Bolg の「匿名デリゲートとラムダ式」を参照してください。上記には非常に多くの広告がありますが、標準の APM モデルが非同期プログラミングを実装する方法を見てみましょう。

IAsyncResultインターフェース

IAsyncResultインターフェースは、非同期関数のステータスを定義します。このインターフェースの具体的な属性と意味は次のとおりです:

   //     表示异步操作的状态。
    [ComVisible(true)]    public interface IAsyncResult
    {        //
        // 摘要:        //     获取一个值,该值指示异步操作是否已完成。        //
        // 返回结果:        //     如果操作已完成,则为 true;否则为 false。
        bool IsCompleted { get; }        //
        // 摘要:        //     获取用于等待异步操作完成的 System.Threading.WaitHandle。        //
        // 返回结果:        //     用于等待异步操作完成的 System.Threading.WaitHandle。
        WaitHandle AsyncWaitHandle { get; }        //
        // 摘要:        //     获取一个用户定义的对象,该对象限定或包含有关异步操作的信息。        //
        // 返回结果:        //     一个用户定义的对象,限定或包含有关异步操作的信息。
        object AsyncState { get; }        //
        // 摘要:        //     获取一个值,该值指示异步操作是否同步完成。        //
        // 返回结果:        //     如果异步操作同步完成,则为 true;否则为 false。
        bool CompletedSynchronously { get; }
    }
ログイン後にコピー

注: モデル例1のManualResetEventは、WaitHandle<br/>APMの伝説的な実装メソッド<br/>を継承しています。 IAsyncResult インターフェイス 最後に、IAsyncResult インターフェイスを実装してシミュレーション サンプルの書き換えを完了します。 コードは次のとおりです。

    public class NewWorker
    {        public class WorkerAsyncResult : IAsyncResult
        {
            AsyncCallback callback;            
            public WorkerAsyncResult(int a,int b, AsyncCallback callback, object asyncState) {
                A = a;
                B = b;
                state = asyncState;                
                this.callback = callback;                
                new Thread(Count).Start(this);
            }            
            public int A { get; set; }            
            public int B { get; set; }            
            public int R { get; private set; }            
            private object state;            
            public object AsyncState
            {                
            get
                {                    
                return state;
                }
            }            
            private ManualResetEvent waitHandle;            
            public WaitHandle AsyncWaitHandle
            {                
            get
                {                    
                if (null == waitHandle)
                    {
                        waitHandle = new ManualResetEvent(false);
                    }                    
                    return waitHandle;
                }
            }            private bool completedSynchronously;            
            public bool CompletedSynchronously
            {                get
                {                    
                return completedSynchronously;
                }
            }            
            private bool isCompleted;            
            public bool IsCompleted
            {                
            get
                {                    
                return isCompleted;
                }
            }            
            private static void Count(object state)
            {                
            var result = state as WorkerAsyncResult;
                result.R = result.A + result.B;
                Thread.Sleep(1000);
                result.completedSynchronously = false;
                result.isCompleted = true;
                ((ManualResetEvent)result.AsyncWaitHandle).Set();                
                if (result.callback != null)
                {
                    result.callback(result);
                }
            }
        }        
        public int Num1 { get; set; }        
        public int Num2 { get; set; }        
        public IAsyncResult BeginWork(AsyncCallback userCallback, object asyncState)
        {
            IAsyncResult result = new WorkerAsyncResult(Num1,Num2,userCallback, asyncState);            
            return result;
        }        public int EndWork(IAsyncResult result)
        {
            WorkerAsyncResult r = result as WorkerAsyncResult;
            r.AsyncWaitHandle.WaitOne();            return r.R;
        }
    }
ログイン後にコピー

サンプル コード分析:

上記のコードの NewWorker の内部クラス WorkerAsyncResult が重要なポイントです。 IAsyncResult インターフェイスを実装し、それによって制御され、計算作業を完了するための新しいスレッドを開始します。

WorkerAsyncResult では、計算に使用される値を保存するために 2 つのプライベート属性 A と B が追加され、WorkerAsyncResult の内部操作の結果を保存するために、外部から読み取り可能で書き込み不可能な属性 R が使用されます。 AsyncWaitHandle プロパティは ManualResetEvent によって動作し、最初のアクセス時に ManualResetEvent が作成されます (ただし、解放されません)。他のインターフェースプロパティは通常通り実装されており、言うことはありません。

新しい静的 Count メソッドが WorkerAsyncResult に追加され、パラメーターの状態は Count メソッドを呼び出す現在の WorkerAsyncResult オブジェクトです。 Count メソッドは WorkerAsyncResult オブジェクトの新しく開始されたスレッドで実行されるため、Thread.Sleep(1000) は新しいスレッドを 1 秒間ブロックします。次に、現在の WorkerAsyncResult オブジェクトが同期的に完了したかどうかを false に設定し、ManualResetEvent 通知を解放して、スレッドが通知を取得して実行状態になるかどうかを確認します。存在する場合はコールバックします。

NewWorker は非常に単純で、Num1 と Num2 の 2 つの属性が計算対象の値です。 BeginWork は WorkerAsyncResult オブジェクトを作成し、計算対象の 2 つの値、Num1、Num2、userCallback コールバック デリゲート、およびオブジェクト型 asyncState を、作成される WorkerAsyncResult オブジェクトに渡します。この手順の後、WorkerAsyncResult オブジェクトは操作に必要なすべてのデータ、操作完了後のコールバックを取得し、操作を実行するための新しいスレッドをすぐに開始します (WorkerAsyncResult.Count メソッドを実行します)。

WorkerAsyncResult.Countは新規スレッドで実行されるため、スレッド外部では新規スレッドの状態を正確に知ることができません。外部スレッドが新しいスレッドと同期する必要があるため、EndWork メソッドが NewWorker に追加され、パラメーターの型は IAsyncResult です。 EndWork メソッドを呼び出すには、BeginWork によって取得された WorkerAsyncResult オブジェクトを渡す必要があります。EndWork メソッドが WorkerAsyncResult オブジェクトを取得した後、WorkerAsyncResult.AsyncWaitHandle.WaitOne() メソッドを呼び出し、通知が取得されるまで待ちます。操作スレッドが操作を完了した (スレッドが終了していない) 場合、次のステップは操作結果 R を取得して返します。

次のステップは、次のような NewWorker 呼び出しプログラムです:

        static void Main(string[] args)
        {
            NewWorker w2 = new NewWorker();
            w2.Num1 = 10;
            w2.Num2 = 12;
            IAsyncResult r = null;
            r = w2.BeginWork((obj) => {
            Console.WriteLine("Thread Id:{0},Count:{1}",Thread.CurrentThread.ManagedThreadId,
            w2.EndWork(r));
            }, null);
            Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);
            Console.ReadLine();
        }
ログイン後にコピー

下の図に単純に描いたプログラム呼び出しプロセスは、友人の理解に役立ちます:

開発者に対応した標準 APM モデルの非同期プログラミング複雑すぎると言いました。したがって、IAsyncResult インターフェースの実装による非同期プログラミングは、凡例ですが、役に立ちません (罪、罪、罪...)。

デリゲート非同期プログラミング (APM 標準実装)

C#中委托天生支持异步调用(APM模型),任何委托对象后"."就会发现BeginInvoke、EndInvoke、Invoke三个方法。BeginInvoke为异步方式调用委托、EndInvoke等待委托的异步调用结束、Invoke同步方式调用委托。因此上面的标准APM实例,可借助 delegate 进行如下简化。

上面NewWorker使用委托方式改写如下:

<br/>
ログイン後にコピー

<br/>

    public class NewWorker2
    {
        Func<int, int, int> action;        public NewWorker2()
        {
            action = new Func<int, int, int>(Work);
        }        public IAsyncResult BeginWork(AsyncCallback callback, object state)
        {            dynamic obj = state;            return action.BeginInvoke(obj.A, obj.B, callback, this);
        }        public int EndWork(IAsyncResult asyncResult)
        {            try
            {                return action.EndInvoke(asyncResult);
            }            catch (Exception ex)
            {                throw ex;
            }
        }        private int Work(int a, int b)
        {
            Thread.Sleep(1000);            return a + b;
        }
    }
ログイン後にコピー

调用程序:

        static void Main(string[] args)
        {
            NewWorker2 w2 = new NewWorker2();
            IAsyncResult r = null;
            r = w2.BeginWork((obj) =>
            {
                Console.WriteLine("Thread Id:{0},Count:{1}", Thread.CurrentThread.ManagedThreadId,
                    w2.EndWork(r));
            }, new { A = 10, B = 11 });
            Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId);

            Console.ReadLine();
        }
ログイン後にコピー

上面的使用委托进行APM异步编程,比实现 IAsyncResult 接口的方式精简太多、更易理解使用。因此这里建议朋友们 delegate 异步调用模型应该掌握起来,而通过实现 IAsyncResult 接口的传说方式看你的喜好吧。 

以上がC# 非同期 APM モードの非同期プログラム開発例の共有の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート