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

C#多線程TPL模式高級(jí)用法探秘

 更新時(shí)間:2022年03月22日 11:00:21   作者:.NET開(kāi)發(fā)菜鳥(niǎo)  
本文詳細(xì)講解了C#多線程TPL模式的高級(jí)用法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

一、引言

我們先來(lái)看下面的一個(gè)小示例:一個(gè)Winfrom程序,界面上有一個(gè)按鈕,有兩個(gè)異步方法,點(diǎn)擊按鈕調(diào)用兩個(gè)異步方法,彈出執(zhí)行順序,代碼如下:

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

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

        /// <summary>
        /// 按鈕點(diǎn)擊事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void btnStart_Click(object sender, EventArgs e)
        {
            string i1 = await F1Async();
            MessageBox.Show("i1=" + i1);
            string i2 = await F2Async();
            MessageBox.Show("i2=" + i2);
        }

        /// <summary>
        /// 異步方法F1
        /// </summary>
        /// <returns></returns>
        private Task<string> F1Async()
        {
            MessageBox.Show("F1 Start");
            return Task.Run<string>(() => 
            {
                // 休眠1秒
                Thread.Sleep(1000);
                MessageBox.Show("F1 Run");
                return "F1";
            });
        }

        /// <summary>
        /// 異步方法F2
        /// </summary>
        /// <returns></returns>
        private Task<string> F2Async()
        {
            MessageBox.Show("F2 Start");
            return Task.Run<string>(() =>
            {
                // 休眠2秒
                Thread.Sleep(2000);
                MessageBox.Show("F2 Run");
                return "F2";
            });
        }
    }
}

在上面的代碼中,Task.Run()是用來(lái)把一個(gè)代碼段包裝為T(mén)ask<T>的方法,Run中委托的代碼體就是異步任務(wù)執(zhí)行的邏輯,最后return返回值。

運(yùn)行程序,可以得到如下的輸出順序:

F1 Start->F1 Run->i1=F1->F2 Start->F2 Run->i2=F2。

我們對(duì)按鈕事件進(jìn)行修改,修改為下面的代碼,在看看執(zhí)行順序:

/// <summary>
/// 按鈕點(diǎn)擊事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnStart_Click(object sender, EventArgs e)
{
    //string i1 = await F1Async();
    //MessageBox.Show("i1=" + i1);
    //string i2 = await F2Async();
    //MessageBox.Show("i2=" + i2);

    Task<string> task1 = F1Async();
    Task<string> task2 = F2Async();
    string i1 = await task1;
    MessageBox.Show("i1=" + i1);
    string i2 = await task2;
    MessageBox.Show("i2=" + i2);
}

再次運(yùn)行程序,查看輸出順序:

F1 Start->F2 Start->F1 Run->i1=F1->F2 Run->i2=F2。

可以看出兩次的執(zhí)行順序不一致。這是什么原因呢?

這是因?yàn)椴⒉皇堑搅薬wait才開(kāi)始執(zhí)行Task異步任務(wù),執(zhí)行到Task<string> task1=F1Async()這句代碼的時(shí)候,F(xiàn)1Async異步任務(wù)就開(kāi)始執(zhí)行了。同理,執(zhí)行到下一句代碼就開(kāi)始執(zhí)行F2Async異步任務(wù)了。await是為了保證執(zhí)行到這里的時(shí)候異步任務(wù)一定執(zhí)行完。執(zhí)行到await的時(shí)候,如果異步任務(wù)還沒(méi)有執(zhí)行,那么就等待異步任務(wù)執(zhí)行完。如果異步任務(wù)已經(jīng)執(zhí)行完了,那么就直接獲取異步任務(wù)的返回值。

我們上面解釋的原因是否正確呢?我們可以做下面的一個(gè)實(shí)驗(yàn),來(lái)驗(yàn)證上面說(shuō)的原因,我們把按鈕事件里面的await注釋掉,看看異步方法還會(huì)不會(huì)執(zhí)行,代碼如下:

/// <summary>
/// 按鈕點(diǎn)擊事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnStart_Click(object sender, EventArgs e)
{
    //string i1 = await F1Async();
    //MessageBox.Show("i1=" + i1);
    //string i2 = await F2Async();
    //MessageBox.Show("i2=" + i2);

    Task<string> task1 = F1Async();
    Task<string> task2 = F2Async();
    // 并不是到了await才開(kāi)始執(zhí)行Task異步任務(wù),這里是保證異步任務(wù)一定執(zhí)行完
    // 代碼執(zhí)行到這里的時(shí)候,如果沒(méi)有執(zhí)行完就等待執(zhí)行完,如果已經(jīng)執(zhí)行完,就直接獲取返回值
    // 這里注釋掉await代碼段,查看異步方法是否會(huì)執(zhí)行
    //string i1 = await task1;
    //MessageBox.Show("i1=" + i1);
    //string i2 = await task2;
    //MessageBox.Show("i2=" + i2);
}

運(yùn)行程序,發(fā)現(xiàn)異步方法還是會(huì)執(zhí)行,這就說(shuō)明我們上面解釋的原因是正確的。感興趣的可以使用Reflector反編譯查看內(nèi)部實(shí)現(xiàn)的原理,主要是MoveNext()方法內(nèi)部。

我們可以得到下面的結(jié)論:

  • 只要方法是Task<T>類(lèi)型的返回值,都可以用await來(lái)等待調(diào)用獲取返回值。
  • 如果一個(gè)返回Task<T>類(lèi)型的方法被標(biāo)記了async,那么只要方法內(nèi)部直接return T這個(gè)類(lèi)型的實(shí)例就可以了。
  • 一個(gè)返回Task<T>類(lèi)型的方法如果沒(méi)有被標(biāo)記為async,那么需要方法內(nèi)部直接return一個(gè)Task的實(shí)例。

上面說(shuō)的第二點(diǎn)看下面的代碼

/// <summary>
/// 方法標(biāo)記為async 直接返回一個(gè)int類(lèi)型的數(shù)值即可
/// </summary>
/// <returns></returns>
private async Task<int> F3Async()
{
    return 2;
}

上面的第三點(diǎn)可以看下面的代碼:

/// <summary>
/// 方法沒(méi)有被標(biāo)記為async,直接返回一個(gè)Task
/// </summary>
/// <returns></returns>
private Task<int> F4Async()
{
    return Task.Run<int>(() => 
    {
        return 2;
    });
}

二、TPL高級(jí)

我們做一些總結(jié):

1、如果方法內(nèi)部有await,則方法必須標(biāo)記為async。await和async是成對(duì)出現(xiàn)的,只有await沒(méi)有async程序會(huì)報(bào)錯(cuò)。只有async沒(méi)有await,程序會(huì)按照同步方法執(zhí)行。

2、ASP.NET MVC中的Action方法和WinForm中的事件處理方法都可以標(biāo)記為async,控制臺(tái)的Main()方法不能被標(biāo)記為async。對(duì)于不能標(biāo)記為async的方法怎么辦呢?我們可以使用Result屬性來(lái)獲取值,看下面代碼:

using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace AsyncDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 實(shí)例化對(duì)象
            HttpClient client = new HttpClient();
            // 調(diào)用異步的Get方法
            Task<HttpResponseMessage> taskMsg = client.GetAsync("http://www.baidu.com");
            // 通過(guò)Result屬性獲取返回值
            HttpResponseMessage msg = taskMsg.Result;
            Task<string> taskRead = msg.Content.ReadAsStringAsync();
            string html = taskRead.Result;
            Console.WriteLine(html);
            Console.ReadKey();
        }
    }
}

不建議使用這種方式,這樣體現(xiàn)不出異步帶來(lái)的好處,而且使用Result屬性,有可能會(huì)帶來(lái)上下文切換造成的死鎖。

下面我們來(lái)看看創(chuàng)建Task的方法。

 1、如果返回值就是一個(gè)立即可以隨手得到的值,那么就用Task.FromResult()??聪旅娲a:

static Task<int> TestAsync()
{
    //return Task.Run<int>(() => 
    //{
    //    return 5;
    //});

    // 簡(jiǎn)便寫(xiě)法
    return Task.FromResult(3);
}

2、如果是一個(gè)需要休息一會(huì)的任務(wù)(比如下載失敗則過(guò)5秒鐘后重試。主線程不休息,和Thread.Sleep不一樣),那么就用Task.Delay()。

3、Task.Factory.FromAsync()會(huì)把IAsyncResult轉(zhuǎn)換為T(mén)ask,這樣APM風(fēng)格的API也可以用await來(lái)調(diào)用。

4、編寫(xiě)異步方法的簡(jiǎn)化寫(xiě)法。如果方法聲明為async,那么可以直接return具體的值,不用在創(chuàng)建Task,由編譯器創(chuàng)建Task,看下面的代碼:

static async Task<int> Test2Async()
{
    // 復(fù)雜寫(xiě)法
    //return await Task.Run<int>(() => 
    //{
    //    return 5;
    //});

    // 下面是簡(jiǎn)化寫(xiě)法,直接返回
    return 6;
}

到此這篇關(guān)于C#多線程TPL模式高級(jí)用法探秘的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • WPF如何自定義TabControl控件樣式示例詳解

    WPF如何自定義TabControl控件樣式示例詳解

    這篇文章主要給大家介紹了關(guān)于WPF如何自定義TabControl控件樣式的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-04-04
  • c#枚舉值增加特性說(shuō)明(推薦)

    c#枚舉值增加特性說(shuō)明(推薦)

    這篇文章主要介紹了c#枚舉值增加特性說(shuō)明(推薦),需要的朋友可以參考下
    2017-05-05
  • C#中Mutex對(duì)象用法分析

    C#中Mutex對(duì)象用法分析

    這篇文章主要介紹了C#中Mutex對(duì)象用法,結(jié)合實(shí)例形式分析了Mutex對(duì)象的功能與線程操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2016-06-06
  • c#數(shù)字圖像處理的3種方法示例分享

    c#數(shù)字圖像處理的3種方法示例分享

    這篇文章主要介紹了c#數(shù)字圖像處理的3種方法示例,需要的朋友可以參考下
    2014-02-02
  • C# 控件屬性和InitializeComponent()關(guān)系案例詳解

    C# 控件屬性和InitializeComponent()關(guān)系案例詳解

    這篇文章主要介紹了C# 控件屬性和InitializeComponent()關(guān)系案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • C#中判斷某類(lèi)型是否可以進(jìn)行隱式類(lèi)型轉(zhuǎn)換

    C#中判斷某類(lèi)型是否可以進(jìn)行隱式類(lèi)型轉(zhuǎn)換

    在我們采用反射動(dòng)態(tài)調(diào)用一些方法時(shí),常常涉及到類(lèi)型的轉(zhuǎn)換,直接判斷類(lèi)型是否相符有時(shí)不能判斷調(diào)用方法是否合適
    2013-04-04
  • C#中DataGridView導(dǎo)出Excel的兩種方法

    C#中DataGridView導(dǎo)出Excel的兩種方法

    這篇文章主要介紹了C#中DataGridView導(dǎo)出Excel的兩種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • C#實(shí)現(xiàn)鼠標(biāo)左右鍵切換效果

    C#實(shí)現(xiàn)鼠標(biāo)左右鍵切換效果

    這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)鼠標(biāo)左右鍵切換功能,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以跟隨小編一起了解一下
    2022-12-12
  • C#基礎(chǔ)入門(mén)之算法:交換

    C#基礎(chǔ)入門(mén)之算法:交換

    本文主要介紹了C#中算法:交換的相關(guān)知識(shí),具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧
    2017-03-03
  • C#基礎(chǔ)知識(shí)之GetType與typeof的區(qū)別小結(jié)

    C#基礎(chǔ)知識(shí)之GetType與typeof的區(qū)別小結(jié)

    在比較對(duì)象時(shí),需要了解他們的類(lèi)型,才能決定他們的值是否能比較。所有的類(lèi)都從System.Object中繼承了GetType()方法,常常與typeo()運(yùn)算符一起使用。這篇文章主要給大家介紹了關(guān)于C#基礎(chǔ)知識(shí)之GetType與typeof區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2021-06-06

最新評(píng)論