溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C#異步多線程中Thread的示例分析

發(fā)布時間:2022-03-04 11:13:10 來源:億速云 閱讀:121 作者:小新 欄目:開發(fā)技術(shù)

這篇文章給大家分享的是有關(guān)C#異步多線程中Thread的示例分析的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

Thread API

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

Instance

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

C#異步多線程中Thread的示例分析

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

C#異步多線程中Thread的示例分析

C#異步多線程中Thread的示例分析

C#異步多線程中Thread的示例分析

接著我們創(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();

C#異步多線程中Thread的示例分析

啟動程序,可以看到線程 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)的代碼,這就是多線程。

C#異步多線程中Thread的示例分析

說到委托可會有小伙伴發(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();

C#異步多線程中Thread的示例分析

Suspend、Resume

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

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

C#異步多線程中Thread的示例分析

C#異步多線程中Thread的示例分析

Abort、ResetAbort

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

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

比如,發(fā)出的動作,可能收不回來。查詢數(shù)據(jù)庫來說,當(dāng)一個查庫命令發(fā)送到數(shù)據(jù)庫,我們在C# 執(zhí)行了 Abort,但查庫這個命令是收不回來的,因為他是在數(shù)據(jù)庫層面,當(dāng)數(shù)據(jù)庫查詢完成只是沒有接收響應(yīng)的線程罷了。

C#異步多線程中Thread的示例分析

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();

C#異步多線程中Thread的示例分析

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 等著也是一種工作。

C#異步多線程中Thread的示例分析

接著我們看下超時等待,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ù),當(dāng)子線程執(zhí)行 3 s 后(期間主線程 1 在等待),主線程 3 開始執(zhí)行任務(wù)了。

C#異步多線程中Thread的示例分析

注意: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();

C#異步多線程中Thread的示例分析

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); // 當(dāng)前線程休息 200 毫秒
}

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

Console.ReadLine();

C#異步多線程中Thread的示例分析

Sleep

線程暫停,Sleep 當(dāng)前線程暫停。如果是 winform 是會卡界面的,當(dāng) 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();

C#異步多線程中Thread的示例分析

IsBackground

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

前臺線程,意思也就是,當(dāng)我們啟動一個程序,當(dāng)關(guān)閉程序時,如果還有子線程執(zhí)行任務(wù),當(dāng)前進程是不會退出的,會等待著子進程將任務(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); // 當(dāng)前線程休息 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)先級,當(dāng)線程從高到低分配了優(yōu)先級,在向 CPU 申請線程時會優(yōu)先分配。但是這個功能也比較雞肋,對于 CPU 而言,當(dāng)他們同時過來,只是會為優(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()}");

感謝各位的閱讀!關(guān)于“C#異步多線程中Thread的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI