線程池,千萬注意,原來很多人都在錯用


線程池是一種多線程處理形式,處理過程中將任務添加到隊列,然后在創建線程后自動啟動這些任務。線程池線程都是后台線程。每個線程都使用默認的堆棧大小,以默認的優先級運行。

[C#線程池]

場景:以下是C# winform寫的一個線程池示例程序。窗體上,分別拖放一個開始和停止按鈕,單擊開始按鈕,for尋覓模擬7000個任務隊列,在線程池運行過程中,可以點擊停止按鈕,來結束線程池所有任務,這里借助CancellationTokenSource對象,來實現線程池的停止。

多線程

開始前,請大家看以下代碼,有什么問題?這里以C# Winform的形式,向大家展現以下代碼:

1、第一種:這是開始按鈕單擊事件的代碼

btnStart.Enabled = false;
            isStop = false;
            listCancels.Clear();//清空
            cancelToken.Dispose();
            cancelToken = new CancellationTokenSource();
            System.Threading.ThreadPool.SetMaxThreads(4, 2);
            System.Threading.ThreadPool.SetMinThreads(2, 1);
            for (int i = 0; i < 7000; i++)
            {
                System.Threading.ThreadPool.QueueUserWorkItem((state) =>
                {
                    while (true)
                    {
                        lock (locker)
                        {
                            if (isStop) { break; };
                            if (cancelToken.IsCancellationRequested) { break; }
                        }

                        int works = 0, maxwork = 0;
                        int port = 0, maxport = 0;

                        System.Threading.ThreadPool.GetAvailableThreads(out works, out port);
                        System.Threading.ThreadPool.GetMaxThreads(out maxwork, out maxport);

                        lblStatus.BeginInvoke(new Action(() =>
                        {
                            lblStatus.Text = System.Threading.Thread.CurrentThread.ManagedThreadId + ":" + Guid.NewGuid();

                        }));
                        System.Threading.Thread.Sleep(100);
                    }

                });
            }

  

2、第二種:看出有何不同了嗎,好像差不多,都很正常對吧

 btnStart.Enabled = false;
            isStop = false;
            listCancels.Clear();//清空
            cancelToken.Dispose();
            cancelToken = new CancellationTokenSource();
            System.Threading.ThreadPool.SetMaxThreads(4, 2);
          
            for (int i = 0; i < 7000; i++)
            {
                System.Threading.ThreadPool.QueueUserWorkItem((state) =>
                {
                    while (true)
                    {
                        lock (locker)
                        {
                            if (isStop) { break; };
                            if (cancelToken.IsCancellationRequested) { break; }
                        }

                        int works = 0, maxwork = 0;
                        int port = 0, maxport = 0;

                        System.Threading.ThreadPool.GetAvailableThreads(out works, out port);
                        System.Threading.ThreadPool.GetMaxThreads(out maxwork, out maxport);

                        lblStatus.BeginInvoke(new Action(() =>
                        {
                            lblStatus.Text = System.Threading.Thread.CurrentThread.ManagedThreadId + ":" + Guid.NewGuid();

                        }));
                        System.Threading.Thread.Sleep(100);
                    }

                });
            }

 

3、第三種:

 btnStart.Enabled = false;
            isStop = false;
            listCancels.Clear();//清空
            cancelToken.Dispose();
            cancelToken = new CancellationTokenSource();
            System.Threading.ThreadPool.SetMinThreads(2, 1);
            System.Threading.ThreadPool.SetMaxThreads(4, 2);
            
            for (int i = 0; i < 7000; i++)
            {
                System.Threading.ThreadPool.QueueUserWorkItem((state) =>
                {
                    while (true)
                    {
                        lock (locker)
                        {
                            if (isStop) { break; };
                            if (cancelToken.IsCancellationRequested) { break; }
                        }

                        int works = 0, maxwork = 0;
                        int port = 0, maxport = 0;

                        System.Threading.ThreadPool.GetAvailableThreads(out works, out port);
                        System.Threading.ThreadPool.GetMaxThreads(out maxwork, out maxport);

                        lblStatus.BeginInvoke(new Action(() =>
                        {
                            lblStatus.Text = System.Threading.Thread.CurrentThread.ManagedThreadId + ":" + Guid.NewGuid();

                        }));
                        System.Threading.Thread.Sleep(100);
                    }

                });
            }

如果沒有看出以上三段代碼有何不同,那就一定要看看下面的內容。

接下來,我們借助操作系統的任務管理器,來及時監測Winform進程所進行的線程數。

如果,是第一種寫法,我們來看看:

結果,並不像我們預想的那樣,即使如此,線程數還在一路飆升,不一會兒竟達到兩百多個線程,這是為何?

第二種,只不過是比第一種少些了一行System.Threading.ThreadPool.SetMinThreads(2, 1);的代碼,結果,

好像沒有什么變化,線程池仍然一路飆升,直到UI線程出現假死。

就在我寫這兩段博客的瞬間,看看任務管理器的情況吧:

什么,線程數601?暫不說它是否精確,但此刻,程序是很不正常的在運行。

     第三種,是在第一種基礎上,調整了兩行代碼的順序,恰是這兩行代碼,讓我們正視了之前線程池誤區。也改變了結果。以第三種方式,我將程序運行6個多小時,一夜不關,程序依然運行平穩,線程數依然保持在較穩的水平。

原來,順序真的很重要。經常看到網上有關C#線程池用法的博客,當我們看到,上來都寫System.Threading.ThreadPool.SetMaxThreads(4, 2);這行代碼時,我們切記要注意了。當任務量小的時候,這種問題並不明顯,依然能很好的完成工作。但是,像我以上的例子,龐大任務隊列加入線程池,每個任務又以漫長的運行周期在運行。那把上面那個例子,搬上去看看,這種寫法必死,切記。

    使用C#線程池的時候,一定要先設置 System.Threading.ThreadPool.SetMinThreads(2, 1);,然后再設置  System.Threading.ThreadPool.SetMaxThreads(4, 2);,這才是C#線程池正確的用法。

    最后給大家介紹一個關鍵詞volatile,不知道的朋友,可以了解下。想要了解更多多線程池方面的信息,請訪問http://www.lichaoqiang.com/


免責聲明!

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



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