欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

.NET異步編程模式的三種類(lèi)型介紹

 更新時(shí)間:2022年03月21日 11:24:57   作者:.NET開(kāi)發(fā)菜鳥(niǎo)  
這篇文章介紹了.NET異步編程模式的三種類(lèi)型,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

一、引言

.NET中很多的類(lèi)、接口在設(shè)計(jì)的時(shí)候都考慮了多線程問(wèn)題,簡(jiǎn)化了多線程程序的開(kāi)發(fā),不用自己去寫(xiě)WaitHandler等這些底層的代碼,由于歷史的發(fā)展,這些類(lèi)的接口設(shè)計(jì)有著三種不同的風(fēng)格:EAP、APM和TPL。目前重點(diǎn)用TPL。

二、EAP

EAP是Event-based Asynchronous Pattem(基于事件的異步模型)的簡(jiǎn)寫(xiě),類(lèi)似于Ajax中的XmlHttpRequest,send之后并不是處理完成了,而是在onreadystatechange事件中再通知處理完成。看下面的一個(gè)示例。

我們創(chuàng)建一個(gè)Winform程序,窗體上面有一個(gè)按鈕和一個(gè)文本框,點(diǎn)擊按鈕開(kāi)始下載,下載完成以后給文本框賦值,界面如圖所示:

開(kāi)始下載代碼如下:

using System;
using System.Net;
using System.Windows.Forms;

namespace EAPDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 開(kāi)始下載
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnDownload_Click(object sender, EventArgs e)
        {
            // 實(shí)例化對(duì)象
            WebClient wc = new WebClient();
            // 注冊(cè)完成事件
            wc.DownloadStringCompleted += Wc_DownloadStringCompleted;
            // 異步下載 不會(huì)阻塞界面,窗體可以隨意拖動(dòng)
            wc.DownloadStringAsync(new Uri("http://www.baidu.com"));
        }

        /// <summary>
        /// 下載完成事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            // 給文本框賦值
            txtContent.Text = e.Result;
        }
    }
}

程序執(zhí)行的時(shí)候,界面可以隨意拖動(dòng),不會(huì)卡住界面,而使用同步方法就會(huì)卡住界面,使用同步方法時(shí)放到一個(gè)單獨(dú)的線程里面也可以實(shí)現(xiàn)不卡住界面,但是那種寫(xiě)法比較復(fù)雜,直接使用異步方法就可以完成。 

EAP的優(yōu)點(diǎn)是簡(jiǎn)單,缺點(diǎn)是當(dāng)實(shí)現(xiàn)復(fù)雜的業(yè)務(wù)的時(shí)候很麻煩,比如下載A成功后再下載B,如果下載B成功再下載C,否則就下載D。

EAP的類(lèi)的特點(diǎn)是:一個(gè)異步方法配合一個(gè)***Completed事件。.NET中基于EAP的類(lèi)比較少,也有更好的替代品,因此了解即可。

三、APM

APM(Asynchronous Programming Model)的縮寫(xiě),是.NET舊版本中廣泛使用的異步編程模型。使用了APM的異步方法會(huì)返回一個(gè)IAsyncResult 對(duì)象,這個(gè)對(duì)象有一個(gè)重要的屬性:AsyncWaitHandle。它是一個(gè)用來(lái)等待異步任務(wù)執(zhí)行結(jié)束的一個(gè)同步信號(hào)??聪旅娴囊粋€(gè)示例。

界面上由一個(gè)按鈕和一個(gè)文本框,點(diǎn)擊按鈕開(kāi)始讀取文件內(nèi)容,讀取完畢之后給文本框賦值,界面如下圖所示:

按鈕代碼如下:

using System;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace APMDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnRead_Click(object sender, EventArgs e)
        {
            // 打開(kāi)文件
            FileStream fs = File.OpenRead("F:/test.txt");
            // 聲明數(shù)組
            byte[] buffer = new byte[1024];
            // BeginRead開(kāi)始讀取
            IAsyncResult aResult = fs.BeginRead(buffer, 0, buffer.Length, null, null);
            //等待任務(wù)執(zhí)行結(jié)束
            aResult.AsyncWaitHandle.WaitOne();
            this.txtContent.Text = Encoding.UTF8.GetString(buffer);
            // 結(jié)束讀取
            fs.EndRead(aResult);
        }
    }
}

因?yàn)槔锩媸褂昧薟aitOne(),這里會(huì)產(chǎn)生等待,如果等待時(shí)候過(guò)長(zhǎng),還是會(huì)產(chǎn)生界面卡頓,所以最好還是放到多線程里面去執(zhí)行,修改后的代碼如下:

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace APMDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnRead_Click(object sender, EventArgs e)
        {
            //// 打開(kāi)文件
            //FileStream fs = File.OpenRead("F:/test.txt");
            //// 聲明數(shù)組
            //byte[] buffer = new byte[1024];
            //// BeginRead開(kāi)始讀取
            //IAsyncResult aResult = fs.BeginRead(buffer, 0, buffer.Length, null, null);
            ////等待任務(wù)執(zhí)行結(jié)束
            //aResult.AsyncWaitHandle.WaitOne();
            //this.txtContent.Text = Encoding.UTF8.GetString(buffer);
            //// 結(jié)束讀取
            //fs.EndRead(aResult);

            #region 多線程
            ThreadPool.QueueUserWorkItem(state => 
            {
                // 打開(kāi)文件
                FileStream fs = File.OpenRead("F:/test.txt");
                // 聲明數(shù)組
                byte[] buffer = new byte[1024];
                // BeginRead開(kāi)始讀取
                IAsyncResult aResult = fs.BeginRead(buffer, 0, buffer.Length, null, null);
                //等待任務(wù)執(zhí)行結(jié)束
                aResult.AsyncWaitHandle.WaitOne();
                this.txtContent.BeginInvoke(new Action(() => 
                {
                    this.txtContent.Text= Encoding.UTF8.GetString(buffer); ;
                }));
                // 結(jié)束讀取
                fs.EndRead(aResult);
            });
            #endregion
        }
    }
}

如果不加 aResult.AsyncWaitHandle.WaitOne() 那么很有可能打印出空白,因?yàn)?BeginRead只是“開(kāi)始讀取”。調(diào)用完成一般要調(diào)用 EndXXX 來(lái)回收資源。

APM 的特點(diǎn)是:方法名字以 BeginXXX 開(kāi)頭,返回類(lèi)型為 IAsyncResult,調(diào)用結(jié)束后需要EndXXX。

.Net 中有如下的常用類(lèi)支持 APM:Stream、SqlCommand、Socket 等。

APM 還是太復(fù)雜,了解即可。

四、TPL

TPL(Task Parallel Library)是.NET 4.0之后帶來(lái)的新特性,更簡(jiǎn)潔,更方便。現(xiàn)在在.NET平臺(tái)下已經(jīng)被廣泛的使用。我們看下面的一個(gè)示例。

界面有一個(gè)開(kāi)始按鈕和一個(gè)文本框,點(diǎn)擊按鈕開(kāi)始讀取文件內(nèi)容,讀取完畢賦值到文本框中,開(kāi)始按鈕代碼如下:

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TPLDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private  void btnRead_Click(object sender, EventArgs e)
        {
            // 打開(kāi)文件
            using (FileStream fs = File.OpenRead("F:/test.txt"))
            {
                // 聲明數(shù)組
                byte[] buffer = new byte[1024];
                // BeginRead開(kāi)始讀取
                Task<int> task = fs.ReadAsync(buffer, 0, buffer.Length);
                // 等待
                task.Wait();
                this.txtContent.Text = Encoding.UTF8.GetString(buffer);
            }
        }
    }
}

執(zhí)行task.Wait()的時(shí)候如果比較耗時(shí),也會(huì)造成界面卡頓,所以最好還是放到一個(gè)單獨(dú)的線程中取執(zhí)行,優(yōu)化后的代碼如下:

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TPLDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private  void btnRead_Click(object sender, EventArgs e)
        {
            //// 打開(kāi)文件
            //using (FileStream fs = File.OpenRead("F:/test.txt"))
            //{
            //    // 聲明數(shù)組
            //    byte[] buffer = new byte[1024];
            //    // BeginRead開(kāi)始讀取
            //    Task<int> task = fs.ReadAsync(buffer, 0, buffer.Length);
            //    // 等待
            //    task.Wait();
            //    this.txtContent.Text = Encoding.UTF8.GetString(buffer);
            //}


            // 開(kāi)啟一個(gè)線程
            ThreadPool.QueueUserWorkItem(state =>
            {
                // 打開(kāi)文件
                using (FileStream fs = File.OpenRead("F:/test.txt"))
                {
                    // 聲明數(shù)組
                    byte[] buffer = new byte[1024];
                    // BeginRead開(kāi)始讀取
                    Task<int> task = fs.ReadAsync(buffer, 0, buffer.Length);
                    // 等待
                    task.Wait();
                    this.txtContent.Invoke(new Action(() =>
                    {
                        this.txtContent.Text = Encoding.UTF8.GetString(buffer);
                    }));
                }
            });
        }
    }
}

這樣用起來(lái)和APM相比的好處是:不需要EndXXX。但是TPL還有更強(qiáng)大的功能,那就是異步方法,我們?cè)诮缑嫔显谠黾右粋€(gè)按鈕用來(lái)演示使用異步方法,其實(shí)現(xiàn)代碼如下:

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TPLDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private  void btnRead_Click(object sender, EventArgs e)
        {
            //// 打開(kāi)文件
            //using (FileStream fs = File.OpenRead("F:/test.txt"))
            //{
            //    // 聲明數(shù)組
            //    byte[] buffer = new byte[1024];
            //    // BeginRead開(kāi)始讀取
            //    Task<int> task = fs.ReadAsync(buffer, 0, buffer.Length);
            //    // 等待
            //    task.Wait();
            //    this.txtContent.Text = Encoding.UTF8.GetString(buffer);
            //}


            // 開(kāi)啟一個(gè)線程
            ThreadPool.QueueUserWorkItem(state =>
            {
                // 打開(kāi)文件
                using (FileStream fs = File.OpenRead("F:/test.txt"))
                {
                    // 聲明數(shù)組
                    byte[] buffer = new byte[1024];
                    // BeginRead開(kāi)始讀取
                    Task<int> task = fs.ReadAsync(buffer, 0, buffer.Length);
                    // 等待
                    task.Wait();
                    this.txtContent.Invoke(new Action(() =>
                    {
                        this.txtContent.Text = Encoding.UTF8.GetString(buffer);
                    }));
                }
            });
        }

        /// <summary>
        /// 異步讀取方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void btnAsync_Click(object sender, EventArgs e)
        {
            // 打開(kāi)文件
            using (FileStream fs = File.OpenRead("F:/test.txt"))
            {
                // 聲明數(shù)組
                byte[] buffer = new byte[1024];
                // BeginRead開(kāi)始讀取
                await fs.ReadAsync(buffer, 0, buffer.Length);
                this.txtContent.Text = Encoding.UTF8.GetString(buffer);
            }
        }
    }
}

使用異步方法,要把方法標(biāo)記為異步的,就是將方法標(biāo)記為async,然后方法中使用await,await表示等待ReadAsync執(zhí)行結(jié)束。

注意:方法中如果使用了await,那么方法就必須標(biāo)記為async,但也不是所有方法都可以被輕松的標(biāo)記為async。WinForm中的事件處理方法都可以被標(biāo)記為async,MVC中的Action方法也可以被標(biāo)記為async,控制臺(tái)的Main方法則不能被標(biāo)記為async。

TPL的特點(diǎn)是:方法都以XXXAsync結(jié)尾,返回值類(lèi)型是泛型的Task<T>。

TPL讓我們可以用線性的方式去編寫(xiě)異步程序,不在需要像EAP中那樣搞一堆回調(diào)、邏輯跳來(lái)跳去了。await現(xiàn)在已經(jīng)被JavaScript借鑒走了!

我們用await實(shí)現(xiàn)下面的一個(gè)功能:先下載A,如果下載的內(nèi)容長(zhǎng)度大于100則下載B,否則下載C。代碼實(shí)現(xiàn)如下:

using System;
using System.Net;
using System.Windows.Forms;

namespace AwaitDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void btnDownload_Click(object sender, EventArgs e)
        {
            WebClient wc = new WebClient();
            string str=await wc.DownloadStringTaskAsync("http://www.baidu.com");
            if(str.Length>100)
            {
                str = await wc.DownloadStringTaskAsync("https://www.jd.com/");
            }
            else
            {
                str = await wc.DownloadStringTaskAsync("http://www.dangdang.com/");
            }
            this.txtContent.Text = str;
        }
    }
}

Task<T>中的T是什么類(lèi)型在每個(gè)方法中都不一樣,具體是什么類(lèi)型要看文檔。

WebClient、Stream、Socket等這些“歷史悠久”的類(lèi)都同時(shí)提供了APM、TPL更改的API,甚至有的還提供了EAP風(fēng)格的API。這里建議盡可能的使用TPL風(fēng)格的。

五、如何編寫(xiě)異步方法

在上面的示例中,我們都是使用的.NET框架給我們提供好的API,如果我們想自己編寫(xiě)異步方法該怎么辦?因?yàn)槟壳癟ask使用最廣泛,所以我們這里以TPL為例講解如何編寫(xiě)自己的異步方法。

首先異步方法的返回值要是Task<T>類(lèi)型,方法里面返回一個(gè)Task類(lèi)型,潛規(guī)則(不要求)是方法名字以Async結(jié)尾,這樣就會(huì)知道這是一個(gè)異步方法??聪旅娴睦樱?/p>

/// <summary>
/// 自定義異步方法,返回類(lèi)型是string
/// </summary>
/// <returns></returns>
private Task<string> TestTask()
{
    return Task.Run<string>(() => 
    {
        // 讓線程休眠3秒,模擬一個(gè)耗時(shí)的操作
        Thread.Sleep(3000);
        return "這是一個(gè)異步方法";
    });
}

這樣就編寫(xiě)好了一個(gè)異步方法。那么怎么使用這個(gè)異步方法呢?使用方法跟我們使用.NET框架提供的異步方法一樣,看下面調(diào)用異步方法的代碼:

using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AwaitDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void btnDownload_Click(object sender, EventArgs e)
        {
            WebClient wc = new WebClient();
            string str=await wc.DownloadStringTaskAsync("http://www.baidu.com");
            if(str.Length>100)
            {
                str = await wc.DownloadStringTaskAsync("https://www.jd.com/");
            }
            else
            {
                str = await wc.DownloadStringTaskAsync("http://www.dangdang.com/");
            }
            this.txtContent.Text = str;
        }

        /// <summary>
        /// 自定義異步方法,返回類(lèi)型是string
        /// </summary>
        /// <returns></returns>
        private Task<string> TestTask()
        {
            return Task.Run<string>(() => 
            {
                // 讓線程休眠3秒,模擬一個(gè)耗時(shí)的操作
                Thread.Sleep(3000);
                return "這是一個(gè)異步方法";
            });
}

        /// <summary>
        /// 調(diào)用自定義的異步方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void btnCall_Click(object sender, EventArgs e)
        {
            string str = await TestTask();
            this.txtContent.Text = str;
        }
    }
}

程序輸出結(jié)果:

這樣就可以調(diào)用自己寫(xiě)的異步方法了。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論