首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 網路程式設計中的多執行緒實戰應用

     編輯:關於C#

前言:為何網路程式離不開執行緒?

在開發功能完善的網路應用程式時,執行緒幾乎是不可或缺的元素。一個稍具規模的網路程式,內部往往運行著多條執行緒。當執行緒數量超過兩條時,我們便稱之為多執行緒應用程式。網路程式之所以與執行緒緊密纏繞,根本原因在於網路通訊充滿不確定性——最常見的便是網路阻塞與等待。若在主執行緒中同步處理這些耗時操作,程式會出現回應遲鈍、運行緩慢、甚至假死等問題。將這些耗時任務移至背景執行緒處理,能顯著提升程式的執行效能、回應速度與擴展性。

然而,執行緒是把雙面刃。盲目濫用不僅無法提升效能,反而可能產生大量「程式垃圾」,甚至導致程式結束時因執行緒未正確銷毀而掛起。因此,若你的代碼邏輯已足夠高效,建議優先考慮單執行緒方案。若決定使用多執行緒,請務必先深入理解 Windows 執行緒的運作原理與機制。

一、C# 中建立與使用執行緒的基礎

在 C# 中,所有執行緒操作均圍繞 System.Threading 命名空間下的 Thread 類別展開。最常用的建構函式語法如下:

public Thread(ThreadStart start);

參數 start 為 ThreadStart 委派,指向執行緒啟動時要執行的方法。Thread 類別還提供豐富的方法與屬性來控制執行緒生命週期:

常用方法

方法說明
Abort()終止執行緒,會拋出 ThreadAbortException
Interrupt()中斷處於 WaitSleepJoin 狀態的執行緒
Join()阻塞呼叫執行緒,直到目標執行緒終止
Sleep(ms)讓當前執行緒暫停指定毫秒數
Start()將執行緒狀態設為 Running,由作業系統排程執行
Suspend()/Resume()掛起/恢復執行緒(已過時,不建議使用)

常用屬性

屬性說明
IsAlive取得執行緒是否仍在執行
IsBackground設定/取得是否為背景執行緒
Priority執行緒排程優先權
ThreadState取得執行緒詳細狀態
Name執行緒名稱,便於除錯

注意: .NET 執行緒執行完畢通常會自動銷毀。若需強制終止,可呼叫 Abort(),但若執行緒持有未釋放的非受控資源,仍可能導致資源洩漏。

二、實戰案例:掃描網段電腦上線狀態

開發與執行環境

  • 作業系統:Windows 2000 Server
  • 開發工具:Visual Studio .NET 2002 (.NET Framework SDK v1.0.3705)

掃描原理

程式透過對指定 IP 範圍進行 DNS 反向解析:若能解析出主機名稱,則判定該 IP 在線;若拋出例外,則判定離線。為直觀展示多執行緒優勢,本文先實作單執行緒版本,再改造為多執行緒版本,最後對比效能。

單執行緒版本實作

介面包含四個 NumericUpDown 組成 IP 區間、一個 ListBox 顯示結果、一個 ProgressBar 顯示進度、一個 Button 觸發掃描。核心邏輯如下:

private void button1_Click(object sender, EventArgs e)
{
    listBox1.Items.Clear();
    DateTime startTime = DateTime.Now;
    string mask = $"{numericUpDown1.Value}.{numericUpDown2.Value}.{numericUpDown3.Value}.";
    int min = (int)numericUpDown4.Value;
    int max = (int)numericUpDown5.Value;
    if (min > max) { MessageBox.Show("IP 區間不合法"); return; }

    progressBar1.Minimum = min;
    progressBar1.Maximum = max;

    for (int i = min; i <= max; i++)
    {
        string ip = mask + i;
        IPAddress ipAddr = IPAddress.Parse(ip);
        try
        {
            IPHostEntry host = Dns.GetHostByAddress(ipAddr);
            listBox1.Items.Add($"{ip} 名稱為:{host.HostName}");
        }
        catch
        {
            listBox1.Items.Add($"{ip} 主機沒有響應!");
        }
        progressBar1.Value = i;
    }
    TimeSpan ts = DateTime.Now - startTime;
    label4.Text = $"{ts.Seconds}秒";
    MessageBox.Show("掃描完成!");
    progressBar1.Value = min;
}

測試掃描 10.138.198.1~10 共 10 個 IP,耗時 43 秒。程式循序執行,總耗時與 IP 數量成正比。

單執行緒掃描結果

多執行緒版本改造

改造需解決兩大難點:

  1. 執行緒無回傳值,需將掃描邏輯封裝為無參數方法。
  2. 執行緒需向主執行緒傳遞結果(IP、主機名),透過委派回呼實現。

步驟 1:匯入命名空間

using System.Threading;

步驟 2:定義委派與共用變數

public delegate void UpdateList(string ip, string hostName);
private DateTime startTime;

步驟 3:建立掃描工作類別 Ping

public class Ping
{
    public UpdateList Callback;
    public string IpAddress;
    public string HostName;

    public void Scan()
    {
        IPAddress ip = IPAddress.Parse(IpAddress);
        try
        {
            IPHostEntry host = Dns.GetHostByAddress(ip);
            HostName = host.HostName;
        }
        catch
        {
            HostName = " 主機沒有響應!";
        }
        Callback?.Invoke(IpAddress, HostName);
    }
}

步驟 4:實作 UI 更新回呼(需執行緒安全)

void UpdateMyList(string ip, string hostName)
{
    lock (listBox1)
    {
        listBox1.Items.Add($"{ip} {hostName}");
        if (progressBar1.Value < progressBar1.Maximum) progressBar1.Value++;
        if (progressBar1.Value == progressBar1.Maximum)
        {
            TimeSpan ts = DateTime.Now - startTime;
            label4.Text = $"{ts.Seconds}秒";
            MessageBox.Show("掃描完成!");
            progressBar1.Value = progressBar1.Minimum;
        }
    }
}

步驟 5:重寫按鈕點擊事件,為每個 IP 建立一條執行緒

private void button1_Click(object sender, EventArgs e)
{
    listBox1.Items.Clear();
    startTime = DateTime.Now;
    string mask = $"{numericUpDown1.Value}.{numericUpDown2.Value}.{numericUpDown3.Value}.";
    int min = (int)numericUpDown4.Value;
    int max = (int)numericUpDown5.Value;
    if (min > max) { MessageBox.Show("IP 區間不合法"); return; }

    int threadCount = max - min + 1;
    Thread[] threads = new Thread[threadCount];
    progressBar1.Minimum = min;
    progressBar1.Maximum = max + 1;
    progressBar1.Value = min;

    for (int i = min; i <= max; i++)
    {
        int idx = max - i;
        var worker = new Ping { IpAddress = mask + i, Callback = new UpdateList(UpdateMyList) };
        threads[idx] = new Thread(new ThreadStart(worker.Scan));
        threads[idx].Start();
    }
}

效能對比結果

同樣掃描 10 個 IP,多執行緒版本耗時僅 10 秒。無論掃描多少 IP,總耗時大致維持在單次 DNS 解析的最長等待時間左右,因為所有解析並行進行。這充分證明多執行緒在網路 I/O 密集型任務中的巨大優勢。

多執行緒掃描結果

總結

  • 掌握 DateTime 計算執行耗時的技巧。
  • 理解網路程式引入多執行緒的動機與收益。
  • 熟練建立多執行緒實例、控制生命週期。
  • 透過委派回呼實現「帶回傳值」的執行緒模式。

多執行緒雖強大,但複雜度亦高。若尚感模糊,建議先鞏固作業系統執行緒原理,再回頭消化本文實作細節。後續文章將深入探討執行緒同步、執行緒池、非同步模式等進階主題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved