[C#] 多線程總結(結合進度條)


(一)使用線程的理由

1、可以使用線程將代碼同其他代碼隔離,提高應用程序的可靠性。

2、可以使用線程來簡化編碼。

3、可以使用線程來實現並發執行。

(二)基本知識

1、進程與線程:進程作為操作系統執行程序的基本單位,擁有應用程序的資源,進程包含線程,進程的資源被線程共享,線程不擁有資源。

2、前台線程和后台線程:通過Thread類新建線程默認為前台線程。當所有前台線程關閉時,所有的后台線程也會被直接終止,不會拋出異常。

3、掛起(Suspend)和喚醒(Resume):由於線程的執行順序和程序的執行情況不可預知,所以使用掛起和喚醒容易發生死鎖的情況,在實際應用中應該盡量少用。

4、阻塞線程:Join,阻塞調用線程,直到該線程終止。

5、終止線程:Abort:拋出 ThreadAbortException 異常讓線程終止,終止后的線程不可喚醒。Interrupt:拋出 ThreadInterruptException 異常讓線程終止,通過捕獲異常可以繼續執行。

6、線程優先級:AboveNormal BelowNormal Highest Lowest Normal,默認為Normal。

(三) 線程生命周期

  1. 未啟動狀態:當線程實例被創建但 Start 方法未被調用時的狀況。
  2. 就緒狀態:當線程准備好運行並等待 CPU 周期時的狀況。
  3. 不可運行狀態死亡狀態:當線程已完成執行或已中止時的狀況。
    • 已經調用 Sleep 方法
    • 已經調用 Wait 方法
    • 通過 I/O 操作阻塞

(四) Thread 常用方法:

  • public void Interrupt()    中斷處於 WaitSleepJoin 線程狀態的線程。
  • public void Join()         在繼續執行標准的 COM 和 SendMessage 消息泵處理期間,阻塞調用線程,直到某個線程終止為止。
  • public void Start()        開始一個線程
  • public static void Sleep(int millisecondsTimeout)    讓線程暫停一段時間

一 普通線程

分為兩種,一種是不需要給子線程傳參數,Thread t = new Thread(new ThreadStart(void () target)); 另一種是要給子線程傳一個參數,Thread t = new Thread(new ParameterizedThreadStart(void (object) target));

// 普通線程
private void btn1_Click(object sender, EventArgs e)
{
    progressBar.Value = 0;
    Thread tt = new Thread(new ThreadStart(DoWork1));
    tt.Name = "不帶參數普通線程";
    tt.Start();
    Thread t = new Thread(new ParameterizedThreadStart(DoWork2));
    t.Name = "帶參數普通線程";
    t.IsBackground = true;
    t.Start(100);
    _msg += "當前線程的執行狀態:" + t.IsAlive + "\r\n";
    _msg += "當前托管線程的唯一標識:" + t.ManagedThreadId + "\r\n";
    _msg += "線程名稱:" + t.Name + "\r\n";
    _msg += "當前線程的狀態:" + t.ThreadState;
    MessageBox.Show("消息:\r\n" + _msg, "提示", MessageBoxButtons.OK);
}
// 線程方法
private void DoWork1()
{
    for (int i = 0; i < 100; i++)
    {
        // 跨線程訪問 UI,BeginInvoke 采用異步委托
        progressBar.BeginInvoke(new EventHandler((sender, e) =>
        {
            progressBar.Value = i;
        }), null);
    }
}
// 線程方法
private void DoWork2(object obj)
{
    for (int i = 0; i < (int)obj; i++)
    {
        progressBar.BeginInvoke(new EventHandler((sender, e) =>
        {
            progressBar.Value = i;
        }), null);
    }
}
普通線程

二  線程池

public static bool QueueUserWorkItem(WaitCallback);

public static bool QueueUserWorkItem(WaitCallback, object);

線程池默認為后台線程(IsBackground)

private void btn3_Click(object sender, EventArgs e)
{
    ThreadPool.QueueUserWorkItem(DoWork2, 100);
    // 或者
    ThreadPool.QueueUserWorkItem((s) =>
    {
        int minWorkerThreads, minCompletionPortThreads, maxWorkerThreads, maxCompletionPortThreads;
        ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads);
        ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);
        MessageBox.Show(String.Format("WorkerThreads = {0} ~ {1}, CompletionPortThreads = {2} ~ {3}",
            minWorkerThreads, maxWorkerThreads, minCompletionPortThreads, maxCompletionPortThreads));
        DoWork2(100);
    });
}
// 線程方法
private void DoWork2(object obj)
{
    for (int i = 0; i < (int)obj; i++)
    {
        // Thread.Sleep(50);
        progressBar.BeginInvoke(new EventHandler((sender, e) =>
        {
            progressBar.Value = i;
        }), null);
    }
}
線程池

三  BackgroundWorker

private void btn4_Click(object sender, EventArgs e)
{
    progressBar.Value = 0;
    BackgroundWorker bw = new BackgroundWorker();
    bw.WorkerReportsProgress = true;// 是否報告進度更新
    // 線程執行
    bw.DoWork += new DoWorkEventHandler((obj, args) =>
    {
        for (int i = 0; i < 100; i++)
        {
            bw.ReportProgress(i);
        }
    });
    // UI主線程顯示進度
    bw.ProgressChanged += (obj, progressChangedEventArgs) =>
    {
        progressBar.Value = progressChangedEventArgs.ProgressPercentage;
    };
    // 線程執行完成后的回調函數
    bw.RunWorkerCompleted += (obj, runWorkerCompletedEventArgs) =>
    {
        MessageBox.Show("子線程執行完成!");
    };
    if (!bw.IsBusy)
    {
        bw.RunWorkerAsync();
    }
}
BackgroundWorker

三  Task(.NET 4.0以上版本)

private void btn5_Click(object sender, EventArgs e)
{
    progressBar.Value = 0;
    Task<bool> t = new Task<bool>(maxValue => DoWork((int)maxValue), progressBar.Maximum);
    t.Start();
    t.Wait();
    // 任務完成后繼續延續任務
    Task cwt = t.ContinueWith(task => MessageBox.Show("The result is " + t.Result));
}
// 線程方法
private bool DoWork(int maxValue)
{
    for (int n = 0; n < maxValue; n++)
    {
        progressBar.BeginInvoke(new EventHandler((sender, e) =>
        {
            progressBar.Value = n;
        }), null);
    }

    return true;
}
Task

四  異步委托

public delegate string MyDelegate(object arg);

private void btn6_Click(object sender, EventArgs e)
{
    MyDelegate myDelegate = new MyDelegate(DoWork3);
    IAsyncResult result = myDelegate.BeginInvoke(100, DoWork2Callback, "回調函數參數");

    // 異步執行完成
    string resultStr = myDelegate.EndInvoke(result);
}

// 線程函數
private string DoWork3(object arg)
{
    for (int n = 0; n < (int)arg; n++)
    {
        progressBar.BeginInvoke(new EventHandler((sender, e) =>
        {
            progressBar.Value = n;
        }), null);
    }

    return "Finished";
}

// 異步回調函數
private void DoWork2Callback(IAsyncResult arg)
{
    MessageBox.Show(arg.AsyncState.ToString());
}
異步委托

五  附 跨線程訪問UI之 SynchronizationContext (同步上下文)

private void btn2_Click(object sender, EventArgs e)
{
    SynchronizationContext context = SynchronizationContext.Current;
    new Thread(() =>
    {
        for (int i = 0; i < 100; i++)
        {
            // Send方法是發送一個異步請求消息
            //context.Send((s) =>
            //{
            //    progressBar.Value = i;
            //}, null);
            // Post方法是發送一個同步請求消息
            context.Post((s) =>
            {
                progressBar.Value = i;
            }, null);
        }
    }).Start();
}
SynchronizationContext

 

六  Task+事件+控制(暫停、繼續):

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

namespace TaskTest
{
    public partial class Form1 : Form
    {
        public event EventHandler<MyEventArgs> MyNotify;
        private delegate void DelegateSetProgress(MyEventArgs e);
        CancellationTokenSource tokenSource;
        CancellationToken token;
        ManualResetEvent reset;

        public Form1()
        {
            InitializeComponent();
            MyNotify += new EventHandler<MyEventArgs>(GetProgress);            
        }

        private void SetProgress(MyEventArgs e)
        {
            if(progressBar1.InvokeRequired)
            {
                Invoke(new DelegateSetProgress(SetProgress), e);
            }
            else
            {
                progressBar1.Value = e.value;
                txtMessage.AppendText(e.text);
            }
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            tokenSource = new CancellationTokenSource();
            token = tokenSource.Token;
            reset = new ManualResetEvent(true); // 初始化為true時執行WaitOne不阻塞
            MyNotify(null, new MyEventArgs() { value = 0, text = $"程序已經啟動\n" });
            Task.Run(() =>
            {
                for (int i = 1; i <= 100; i++)
                {
                    if (token.IsCancellationRequested) return;
                    reset.WaitOne();
                    MyNotify(null, new MyEventArgs() { value = i, text = $"進度為:{i}/100 \n" });
                    Thread.Sleep(100);
                }
            },token);
        }

        private void GetProgress(object sender,EventArgs e)
        {
            SetProgress(e as MyEventArgs);
        }

        private void btnPause_Click(object sender, EventArgs e)
        {
            btnPause.Text = btnPause.Text == "暫停" ? "繼續" : "暫停";
            if (btnPause.Text == "暫停")
                reset.Set();
            else
                reset.Reset();
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            tokenSource.Cancel();
        }
    }

    public class MyEventArgs : EventArgs
    {
        public int value = 0;
        public string text = string.Empty;
    }
}

 

 

七  參考資料:

☆參考博客 http://www.cnblogs.com/luxiaoxun/p/3280146.html

☆多線程講解 http://www.w3cschool.cc/csharp/csharp-multithreading.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM