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

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

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

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

Thread API

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

Instance

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

在這里插入圖片描述

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

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

接著我們創(chuàng)建一個簡單的案例,啟動一個線程,模擬做一些任務(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();

在這里插入圖片描述

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

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

在這里插入圖片描述

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

因為在這個版本還沒有 Action、Func,這是在 .Net 3.0 時代的產(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 喚醒,這兩個是一對相互對應(yīng)的 API,使用時這兩個容易產(chǎn)生死鎖,其實在實際中也是不應(yīng)該使用的,.NET 框架已經(jīng)拋棄了,說的很清楚了。

為什么會死鎖呢?比如你開啟了一個子線程 01,對 A 文件進行讀寫操作,此時你對子線程 01 進行了掛起。當你另外一個線程對 02 A 文件進行操作時,此時提示會 A 文件被占用,就行形成死鎖。

在這里插入圖片描述

在這里插入圖片描述

Abort、ResetAbort

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

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

比如,發(fā)出的動作,可能收不回來。查詢數(shù)據(jù)庫來說,當一個查庫命令發(fā)送到數(shù)據(jù)庫,我們在C# 執(zhí)行了 Abort,但查庫這個命令是收不回來的,因為他是在數(shù)據(jù)庫層面,當數(shù)據(jù)庫查詢完成只是沒有接收響應(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(); // 銷毀,方式是拋異常,不一定及時
}
catch (Exception ex)
{
    //Thread.ResetAbort(); // 取消異常
}

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

Console.ReadLine();

在這里插入圖片描述

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

Join

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

例如:threadStart 我們模擬任務(wù)耗時 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ù)耗時 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();

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

在這里插入圖片描述

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

例如:threadStart 我們模擬任務(wù)耗時 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ù)耗時 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();

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

在這里插入圖片描述

注意:thread.Join(n * 1000) 并不是一定會等待那么長時間,而是最多等待,期間子線程任務(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ù)耗時 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 也可以做線程等待,等待的過程中主線程處于閑置狀態(tài)等著子線程完成任務(wù)。如果是 winform 是會卡界面的,主線程等待也是一種工作。

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ù)耗時 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); // 當前線程休息 200 毫秒
}

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

Console.ReadLine();

在這里插入圖片描述

Sleep

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

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

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

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

Console.ReadLine();

在這里插入圖片描述

IsBackground

是否是后臺線程,當實例 Thread 時,默認是前臺線程(IsBackground == false )。前臺線程一定要任務(wù)完成,才會讓進程退出。后臺線程(IsBackground == true)會隨著進程的結(jié)束而結(jié)束,無論子線程任務(wù)是否完成。

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

例如:前臺線程,啟動控制臺后,主線程執(zhí)行完任務(wù)后,會等待子線程任務(wù)完成(5s)后,窗口才會被關(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ù)耗時 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); // 當前線程休息 200 毫秒
    }

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

例如:后臺線程,啟動控制臺后,主線程任務(wù)執(zhí)行完畢后,窗口會立馬被關(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ù)耗時 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)先級,當線程從高到低分配了優(yōu)先級,在向 CPU 申請線程時會優(yōu)先分配。但是這個功能也比較雞肋,對于 CPU 而言,當他們同時過來,只是會為優(yōu)先級高的先分進行分片,但優(yōu)先級低的并不是不會分配,也不代表優(yōu)先級高的就會先執(zhí)行完成,這也取決執(zhí)行的任務(wù)量。其實優(yōu)先級也沒什么用,多線程本來就是無序的。

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ù)耗時 5 秒

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

Thread thread = new Thread(threadStart);
thread.Priority = ThreadPriority.Highest;// CPU 會先執(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é)

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

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

相關(guān)文章

最新評論