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

C#?異步多線程入門到精通之Thread篇

 更新時(shí)間:2021年11月22日 14:47:42   作者:菜鳥厚非  
Thread?是?1.0?時(shí)代的產(chǎn)物,當(dāng)時(shí)?C#?就考慮到了多線程,于是就設(shè)計(jì)了?Thread?。其實(shí)現(xiàn)在不推薦使用,除非在維護(hù)老的項(xiàng)目已經(jīng)用了的。Thread?也是比較雞肋的,有很多缺陷,但也并不是一無(wú)是處

上一篇:C# 異步多線程入門基礎(chǔ)
下一篇:C# 異步多線程入門到精通之ThreadPool篇

Thread API

這里對(duì) Thread 的一些常用 API 進(jìn)行介紹,使用一些案例進(jìn)行說(shuō)明。由于 Thread 的不可控與效率問題,Thread 現(xiàn)在已經(jīng)不常用了,這里介紹一些 API ,想更深入的同學(xué)可以繼續(xù)研究研究。

Instance

首先看 Thread 的構(gòu)造函數(shù),有 ThreadStart 、ParameterizedThreadStart 、maxStackSize 類型的參數(shù),這三個(gè)常用的也就 ThreadStart ,其他兩個(gè)可以作為了解。

在這里插入圖片描述

分別 F12 查看 ThreadStart、ParameterizedThreadStart ,可以看到 ThreadStart 是無(wú)參數(shù)類型的委托、ParameterizedThreadStart 是有參數(shù)類型的委托。maxStackSize 是指定線程占用的最大內(nèi)存數(shù)。

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

接著我們創(chuàng)建一個(gè)簡(jiǎn)單的案例,啟動(dòng)一個(gè)線程,模擬做一些任務(wù)行。如下代碼

Console.WriteLine($"Main 方法開始,ThreadId:{Thread.CurrentThread.ManagedThreadId}");

ThreadStart threadStart = () =>
{
    Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
    // 做一些任務(wù)


    Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId}");
};
Thread thread = new Thread(threadStart);
thread.Start();

Console.WriteLine($"Main 方法結(jié)束,ThreadId:{Thread.CurrentThread.ManagedThreadId}");

Console.ReadLine();

在這里插入圖片描述

啟動(dòng)程序,可以看到線程 1(主線程),沒有等待線程 3(子線程)執(zhí)行完成匿名方法內(nèi)的任務(wù),再執(zhí)行 Main 結(jié)束這段代碼。如果使用的是 winform 是不會(huì)卡界面的。

這就是異步多線程,異步在于線程 1 并沒有等待線程 3 執(zhí)行完成任務(wù),再執(zhí)行線程 1 內(nèi)的下一行,而是讓線程 3 在不影響線程 1 執(zhí)行任務(wù)的情況下執(zhí)行,這就是異步。多線程在于我們啟動(dòng)了一個(gè)線程 3(子線程),在 Main 方法由線程1(子線程)與線程 3(主線程)一起完成 Main 方法內(nèi)的代碼,這就是多線程。

在這里插入圖片描述

說(shuō)到委托可會(huì)有小伙伴發(fā)出疑問,為啥不用 Action ?

因?yàn)樵谶@個(gè)版本還沒有 Action、Func,這是在 .Net 3.0 時(shí)代的產(chǎn)物,Action、Func 的出現(xiàn)就是為了統(tǒng)一,也是為了解決此類問題。

在 dotnet 框架,也建議最好使用 Action、Func,所以,在這使用 Action 是不可以的。如下

Console.WriteLine($"Main 方法開始,ThreadId:{Thread.CurrentThread.ManagedThreadId}");

Action action = () =>
{
    Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
    // 做一些任務(wù)


    Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId}");
};

ThreadStart threadStart = action;

Thread thread = new Thread(threadStart);
thread.Start();

Console.WriteLine($"Main 方法結(jié)束,ThreadId:{Thread.CurrentThread.ManagedThreadId}");

Console.ReadLine();

在這里插入圖片描述

Suspend、Resume

Suspend 掛起、Resume 喚醒,這兩個(gè)是一對(duì)相互對(duì)應(yīng)的 API,使用時(shí)這兩個(gè)容易產(chǎn)生死鎖,其實(shí)在實(shí)際中也是不應(yīng)該使用的,.NET 框架已經(jīng)拋棄了,說(shuō)的很清楚了。

為什么會(huì)死鎖呢?比如你開啟了一個(gè)子線程 01,對(duì) A 文件進(jìn)行讀寫操作,此時(shí)你對(duì)子線程 01 進(jìn)行了掛起。當(dāng)你另外一個(gè)線程對(duì) 02 A 文件進(jìn)行操作時(shí),此時(shí)提示會(huì) A 文件被占用,就行形成死鎖。

在這里插入圖片描述

在這里插入圖片描述

Abort、ResetAbort

Abort 銷毀,很多人在使用,這種是拋異常方式,使子線程銷毀結(jié)束。這個(gè)功能也比較雞肋,Abort 時(shí)子線程并不能立即停止,往往會(huì)有一些延遲,那這個(gè)銷毀有時(shí)也不能達(dá)到我們可控的效果。

比如,在一個(gè)方法內(nèi)開了一個(gè)子線程進(jìn)行數(shù)據(jù)計(jì)算,但執(zhí)行的時(shí)間太長(zhǎng)了,我們等待了 5000 ms,此時(shí) Abort 子線程,是不能立馬讓子線程停止計(jì)算,而是可能要等一會(huì)才能結(jié)束子線程。

比如,發(fā)出的動(dòng)作,可能收不回來(lái)。查詢數(shù)據(jù)庫(kù)來(lái)說(shuō),當(dāng)一個(gè)查庫(kù)命令發(fā)送到數(shù)據(jù)庫(kù),我們?cè)贑# 執(zhí)行了 Abort,但查庫(kù)這個(gè)命令是收不回來(lái)的,因?yàn)樗窃跀?shù)據(jù)庫(kù)層面,當(dāng)數(shù)據(jù)庫(kù)查詢完成只是沒有接收響應(yīng)的線程罷了。

在這里插入圖片描述

Abort 不建議使用,如果使用,一定要 try catch 一下。

Console.WriteLine($"Main 方法開始,ThreadId:{Thread.CurrentThread.ManagedThreadId}");

ThreadStart threadStart = () =>
{
    Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
    // 做一些任務(wù)

    Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId}");
};
Thread thread = new Thread(threadStart);

thread.Start();

try
{
    thread.Abort(); // 銷毀,方式是拋異常,不一定及時(shí)
}
catch (Exception ex)
{
    //Thread.ResetAbort(); // 取消異常
}

Console.WriteLine($"Main 方法結(jié)束,ThreadId:{Thread.CurrentThread.ManagedThreadId}");

Console.ReadLine();

在這里插入圖片描述

Suspend、Resume、Abort 這幾個(gè)方法不建議使用,操作線程暫停、銷毀或者其他操作都是不可控的,應(yīng)為線程本身是操作系統(tǒng)的, CPU 分時(shí)分片會(huì)按照自己的規(guī)則進(jìn)行運(yùn)行,此時(shí)已經(jīng)不是程序可以進(jìn)行控的了。 既然設(shè)計(jì)了 Thread 不可能一無(wú)是處,接下來(lái)我們說(shuō)些有用的

Join

線程等待 ,Join 可以一直等,也可以設(shè)置超時(shí),超時(shí)就是等待一定時(shí)間,就不等了。等待的過(guò)程中主線程處于閑置狀態(tài)等著子線程完成任務(wù)。如果是 winform 是會(huì)卡界面的,主線程等待也是一種工作。

例如:threadStart 我們模擬任務(wù)耗時(shí) 5 秒,在 thread.Start() 任務(wù)開始后,使用 thread.Join() 等著子線程完成工作

Console.WriteLine($"Main 方法開始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

ThreadStart threadStart = () =>
{
    Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
    // 做一些任務(wù)
    Thread.Sleep(5 * 1000); // 模擬任務(wù)耗時(shí) 5 秒

    Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
};

Thread thread = new Thread(threadStart);
thread.Start();

thread.Join();

Console.WriteLine($"Main 方法結(jié)束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

Console.ReadLine();

啟動(dòng)程序,可以看到是我們想要的結(jié)果(與同步執(zhí)行一樣),主線程 1 一直等著 子線程 3 完成執(zhí)行的任務(wù)。如果是 winform 是會(huì)卡界面的,雖然 thread.Join() 主線程 1 會(huì)等著子線程 3 完成工作,但主線程 1 等著也是一種工作。

在這里插入圖片描述

接著我們看下超時(shí)等待,Join 的重載方法

例如:threadStart 我們模擬任務(wù)耗時(shí) 5 秒,在 thread.Start() 任務(wù)開始后,使用 thread.Join(3*1000) ,讓主線程最多等子線程 3 秒,如果 3 秒子線程還未完成任務(wù),就不等待了

Console.WriteLine($"Main 方法開始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

ThreadStart threadStart = () =>
{
    Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
    // 做一些任務(wù)
    Thread.Sleep(5 * 1000); // 模擬任務(wù)耗時(shí) 5 秒

    Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
};

Thread thread = new Thread(threadStart);
thread.Start();

thread.Join(3 * 1000);

Console.WriteLine($"Main 方法結(jié)束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

Console.ReadLine();

啟動(dòng)程序,主線程 1 開始任務(wù),子線程 3 也開始任務(wù),當(dāng)子線程執(zhí)行 3 s 后(期間主線程 1 在等待),主線程 3 開始執(zhí)行任務(wù)了。

在這里插入圖片描述

注意:thread.Join(n * 1000) 并不是一定會(huì)等待那么長(zhǎng)時(shí)間,而是最多等待,期間子線程任務(wù)執(zhí)行完成后,就不等待了。

例如:threadStart 任務(wù)方法模擬 5 s,thread.Join(7 * 1000) 主線程等待 7 s

Console.WriteLine($"Main 方法開始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

ThreadStart threadStart = () =>
{
    Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
    // 做一些任務(wù)
    Thread.Sleep(5 * 1000); // 模擬任務(wù)耗時(shí) 5 秒

    Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
};

Thread thread = new Thread(threadStart);
thread.Start();

thread.Join(7 * 1000);

Console.WriteLine($"Main 方法結(jié)束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

Console.ReadLine();

在這里插入圖片描述

ThreadState

線程狀態(tài),ThreadState 也可以做線程等待,等待的過(guò)程中主線程處于閑置狀態(tài)等著子線程完成任務(wù)。如果是 winform 是會(huì)卡界面的,主線程等待也是一種工作。

Console.WriteLine($"Main 方法開始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

ThreadStart threadStart = () =>
{
    Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
    // 做一些任務(wù)
    Thread.Sleep(5 * 1000); // 模擬任務(wù)耗時(shí) 5 秒

    Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
};

Thread thread = new Thread(threadStart);
thread.Start();

while (thread.ThreadState != ThreadState.Stopped)
{
    Thread.Sleep(200); // 當(dāng)前線程休息 200 毫秒
}

Console.WriteLine($"Main 方法結(jié)束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

Console.ReadLine();

在這里插入圖片描述

Sleep

線程暫停,Sleep 當(dāng)前線程暫停。如果是 winform 是會(huì)卡界面的,當(dāng) Sleep 時(shí),CPU 分片就交出去了,主線程并不在工作狀態(tài)。

Console.WriteLine($"Main 方法開始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

Thread.Sleep(5 * 1000); // 模擬任務(wù)耗時(shí) 5 秒

Console.WriteLine($"Main 方法結(jié)束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

Console.ReadLine();

在這里插入圖片描述

IsBackground

是否是后臺(tái)線程,當(dāng)實(shí)例 Thread 時(shí),默認(rèn)是前臺(tái)線程(IsBackground == false )。前臺(tái)線程一定要任務(wù)完成,才會(huì)讓進(jìn)程退出。后臺(tái)線程(IsBackground == true)會(huì)隨著進(jìn)程的結(jié)束而結(jié)束,無(wú)論子線程任務(wù)是否完成。

前臺(tái)線程,意思也就是,當(dāng)我們啟動(dòng)一個(gè)程序,當(dāng)關(guān)閉程序時(shí),如果還有子線程執(zhí)行任務(wù),當(dāng)前進(jìn)程是不會(huì)退出的,會(huì)等待著子進(jìn)程將任務(wù)執(zhí)行完成,也就是會(huì)阻止進(jìn)程結(jié)束,反之亦然。

例如:前臺(tái)線程,啟動(dòng)控制臺(tái)后,主線程執(zhí)行完任務(wù)后,會(huì)等待子線程任務(wù)完成(5s)后,窗口才會(huì)被關(guān)閉

static void Main(string[] args)
{
    Console.WriteLine($"Main 方法開始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

    ThreadStart threadStart = () =>
    {
        Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
        // 做一些任務(wù)
        Thread.Sleep(5 * 1000); // 模擬任務(wù)耗時(shí) 5 秒

        Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
    };

    Thread thread = new Thread(threadStart);
    thread.Start();

    while (thread.ThreadState != ThreadState.Stopped)
    {
        Thread.Sleep(200); // 當(dāng)前線程休息 200 毫秒
    }

    Console.WriteLine($"Main 方法結(jié)束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
}

例如:后臺(tái)線程,啟動(dòng)控制臺(tái)后,主線程任務(wù)執(zhí)行完畢后,窗口會(huì)立馬被關(guān)閉

static void Main(string[] args)
{
    Console.WriteLine($"Main 方法開始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

    ThreadStart threadStart = () =>
    {
        Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
        // 做一些任務(wù)
        Thread.Sleep(5 * 1000); // 模擬任務(wù)耗時(shí) 5 秒

        Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
    };

    Thread thread = new Thread(threadStart);
    thread.IsBackground = true;
    thread.Start();

    Console.WriteLine($"thread IsBackground:{thread.IsBackground},DateTime:{DateTime.Now.ToLongTimeString()}");

    Console.WriteLine($"Main 方法結(jié)束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
}

Priority

線程可以設(shè)置優(yōu)先級(jí),當(dāng)線程從高到低分配了優(yōu)先級(jí),在向 CPU 申請(qǐng)線程時(shí)會(huì)優(yōu)先分配。但是這個(gè)功能也比較雞肋,對(duì)于 CPU 而言,當(dāng)他們同時(shí)過(guò)來(lái),只是會(huì)為優(yōu)先級(jí)高的先分進(jìn)行分片,但優(yōu)先級(jí)低的并不是不會(huì)分配,也不代表優(yōu)先級(jí)高的就會(huì)先執(zhí)行完成,這也取決執(zhí)行的任務(wù)量。其實(shí)優(yōu)先級(jí)也沒什么用,多線程本來(lái)就是無(wú)序的。

Console.WriteLine($"Main 方法開始,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

ThreadStart threadStart = () =>
{
    Console.WriteLine($"Task Start ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
    // 做一些任務(wù)
    Thread.Sleep(5 * 1000); // 模擬任務(wù)耗時(shí) 5 秒

    Console.WriteLine($"Task End ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");
};

Thread thread = new Thread(threadStart);
thread.Priority = ThreadPriority.Highest;// CPU 會(huì)先執(zhí)行,不代表 Highest 就最優(yōu)先
thread.Start();

Console.WriteLine($"thread IsBackground:{thread.IsBackground},DateTime:{DateTime.Now.ToLongTimeString()}");

Console.WriteLine($"Main 方法結(jié)束,ThreadId:{Thread.CurrentThread.ManagedThreadId},DateTime:{DateTime.Now.ToLongTimeString()}");

總結(jié)

其實(shí)現(xiàn)在來(lái)說(shuō) ,1.0 時(shí)代的 Thread 已經(jīng)沒有什么優(yōu)勢(shì),現(xiàn)在 Thread 唯一有意義的就是 IsBackground = false,這個(gè)前線程(前臺(tái)線程會(huì)阻礙進(jìn)程的退出),后續(xù)的多線程設(shè)計(jì)都是后臺(tái)線程,沒有前臺(tái)線程這個(gè)功能設(shè)計(jì)。

到此這篇關(guān)于C# 異步多線程入門到精通之Thread篇的文章就介紹到這了,更多相關(guān)C# Thread內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論