C#多線程

一、引言

1.1 線程概念

如果對什麼是線程、什麼是進程仍存有疑惑,請先Google之,因爲這兩個概念不在本文的範圍之內。

一些基本概念:
* 多線程:指的是這個程序(一個進程)運行時產生了不止一個線程
* 並行:多個cpu實例或者多臺機器同時執行一段處理邏輯,是真正的同時。
* 併發:通過cpu調度算法,讓用戶看上去同時執行,實際上從cpu操作層面不是真正的同時。併發往往在場景中有公用的資源,那麼針對這個公用的資源往往產生瓶頸,我們會用TPS或者QPS來反應這個系統的處理能力。
* 線程安全:經常用來描繪一段代碼。指在併發的情況之下,該代碼經過多線程使用,線程的調度順序不影響任何結果。這個時候使用多線程,我們只需要關注系統的內存,cpu是不是夠用即可。反過來,線程不安全就意味着線程的調度順序會影響最終結果。
* 同步:通過人爲的控制和調度,保證共享資源的多線程訪問成爲線程安全,來保證結果的準確。

1.1.1 併發

多線程與多進程是併發的兩種途徑。

1.1.1.1 多進程:

多個進程獨立地運行,它們之間通過進程間常規的通信渠道傳遞訊息(信號,套接字,文件,管道等),這種進程間通信不是設置複雜就是速度慢,這是因爲爲了避免一個進程去修改另一個進程,操作系統在進程間提供了一定的保護措施,當然,這也使得編寫安全的併發代碼更容易。
運行多個進程也需要固定的開銷:進程的啓動時間,進程管理的資源消耗。

1.1.1.2 多線程

在單個進程中運行多個線程也可以併發。線程就像輕量級的進程,每個線程相互獨立運行,但它們共享地址空間,所有線程訪問到的大部分數據如指針、對象引用或其他數據可以在線程之間進行傳遞,它們都可以訪問全局變量。進程之間通常共享內存,但這種共享通常難以建立且難以管理,缺少線程間數據的保護。因此,在多線程編程中,我們必須確保每個線程鎖訪問到的數據是一致的。

1.2 線程狀態

https://www.cnblogs.com/wxd0108/p/5479442.html

本文主要示例代碼:
https://github.com/landbroken/BasicKnowledge/tree/master/ThreadCSharp

二、多線程

未完待續

三、線程池

3.1 概念

百度百科:線程池是一種多線程處理形式,處理過程中將任務添加到隊列,然後在創建線程後自動啓動這些任務。線程池線程都是後臺線程。每個線程都使用默認的堆棧大小,以默認的優先級運行,並處於多線程單元中。如果某個線程在託管代碼中空閒(如正在等待某個事件),則線程池將插入另一個輔助線程來使所有處理器保持繁忙。如果所有線程池線程都始終保持繁忙,但隊列中包含掛起的工作,則線程池將在一段時間後創建另一個輔助線程但線程的數目永遠不會超過最大值。超過最大值的線程可以排隊,但他們要等到其他線程完成後才啓動。

3.2 主要方法

線程池主要方法:

// 參數:
        // workerThreads:
        // 要由線程池根據需要創建的新的最小工作程序線程數。
        // completionPortThreads:
        // 要由線程池根據需要創建的新的最小空閒異步 I/O 線程數。
        // 返回結果:如果更改成功,則爲 true;否則爲 false。
        [SecuritySafeCritical]
        public static bool SetMinThreads(int workerThreads, int completionPortThreads);
        // 參數:
        // workerThreads:
        // 線程池中輔助線程的最大數目。
         // completionPortThreads:
        // 線程池中異步 I/O 線程的最大數目。
        // 返回結果:如果更改成功,則爲 true;否則爲 false。
        [SecuritySafeCritical]
        public static bool SetMaxThreads(int workerThreads, int completionPortThreads);

3.3 示例

以下是示例,用了信號燈AutoResetEvent 來判斷線程池內是否運行終止:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ThreadCSharp
{
    public partial class Form1 : Form
    {
        const int cycleNum = 10;
        static AutoResetEvent myEvent = new AutoResetEvent(false);

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        public void testFun(object obj)
        {
            string output = $"{"Begin:" + DateTime.Now.ToString()}:第{obj.ToString()}個線程";
            SetMessage(textBox1, output);
            Thread.Sleep(3000);
            string end = $"{"End:" + DateTime.Now.ToString()}:第{obj.ToString()}個線程";
            SetMessage(textBox1, end);
            if (obj.ToString() == "10")
            {
                myEvent.Set();
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            SetMessage(textBox1, $"主線程執行!{DateTime.Now.ToString()}");
            ThreadPool.SetMinThreads(1, 1);
            ThreadPool.SetMaxThreads(5, 5);
            for (int i = 1; i <= cycleNum; i++)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(testFun), i.ToString());
            }
            SetMessage(textBox1, $"主線程結束!{DateTime.Now.ToString()}");
            myEvent.WaitOne();//阻止當前線程,直到收到信號
            SetMessage(textBox1, $"線程池終止!{DateTime.Now.ToString()}");
        }

        delegate void SetMessageCallBack(TextBox txtIn, string MyMessage);
        private void SetMessage(TextBox txtIn, string MyMessageIn)
        {
            try
            {
                if (!MyMessageIn.EndsWith(Environment.NewLine))
                {
                    MyMessageIn += Environment.NewLine;//加上換行符
                }
                if (this.InvokeRequired)
                {
                    SetMessageCallBack tmpMessage = new SetMessageCallBack(SetMessage);
                    this.BeginInvoke(tmpMessage, new object[] { txtIn, MyMessageIn });
                }
                else
                {
                    txtIn.Text += MyMessageIn;
                }
            }
            catch (Exception ex)
            {
                //線程時間太短,容易在關閉窗口時引起異常:
                //無法訪問已釋放的對象。對象名:“Form1”。
                string tmp = ex.Message;
            }
        }
    }
}

運行結果:

主線程執行!2018/7/28 14:50:09
主線程結束!2018/7/28 14:50:09
線程池終止!2018/7/28 14:50:15
Begin:2018/7/28 14:50:09:第2個線程
Begin:2018/7/28 14:50:09:第3個線程
Begin:2018/7/28 14:50:09:第4個線程
Begin:2018/7/28 14:50:09:第1個線程
Begin:2018/7/28 14:50:10:第5個線程
End:2018/7/28 14:50:12:第2個線程
Begin:2018/7/28 14:50:12:第6個線程
End:2018/7/28 14:50:12:第3個線程
Begin:2018/7/28 14:50:12:第7個線程
End:2018/7/28 14:50:12:第4個線程
Begin:2018/7/28 14:50:12:第8個線程
End:2018/7/28 14:50:12:第1個線程
Begin:2018/7/28 14:50:12:第9個線程
End:2018/7/28 14:50:13:第5個線程
Begin:2018/7/28 14:50:13:第10個線程
End:2018/7/28 14:50:15:第6個線程
End:2018/7/28 14:50:15:第7個線程
End:2018/7/28 14:50:15:第8個線程
End:2018/7/28 14:50:15:第9個線程
End:2018/7/28 14:50:16:第10個線程

可以看出,線程池裏線程的執行不影響主線程的運行,並且在線程池參數設置中

ThreadPool.SetMaxThreads(5, 5);

因此,先同時開始前5個線程(1-5),其它線程在排隊中。然後每執行完一個線程,開始一個新的線程

End:2018/7/28 14:50:12:第2個線程
Begin:2018/7/28 14:50:12:第6個線程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章