文章出處

 接上文 多線程編程學習筆記——線程同步(一)

 接上文 多線程編程學習筆記——線程同步(二)

  接上文 多線程編程學習筆記——線程同步(三)

 

 

      創建多線程操作是非常昂貴的,所以每個運行時間非常短的操作,創建多線程進行操作,可能并不能提高效率,反而降低了效率。

      如果你有非常多的執行時間非常短的操作,那么適合作用線程池來提高效率,而不是自行創建多線程。

      線程池,就是我們先分配一些資源到池子里,當我們需要使用時,則從池子中獲取,用完了,再放回池子里。

     .NET中的線程池是受CLR管理的,TheadTool類有一個QueueUserWorkItem靜態方法,這個靜態方法接受一個委托,代表用戶自定義的一個異步操作,在這個方法被調用之后,委托會進入到內部隊列中,如果池中沒有線程,則創建一個工作線程,把第一個委托放入工作線程。如果繼續放入委托,則池創建新的工作線程,直到工作線程數量達到上限。這時再放入委托,則不會創建新的工作線程,而是在隊列中等待,直到有空閑的工作線程。

       當線程池中所有操作都完成,而且沒有新任務操作時,線程池會釋放長時間不用的資源。

       注意:放入線程池中的操作需要的時間要短,不要把需要長時間運行的操作放入線程池中,或阻塞工作線程。這將導致性能問題和非常難以調用的問題。

                 在ASP.NET中使用線程池要當心,ASP.NET中的線程池是一個共用線程池,如果線程池中的工作線程都用完了,則會造成WEB服務器對正常的HTTP請求無法提供服務。

 

一、     線程池中調用委托

 1.代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; 

namespace ThreadPoolDemo
{
 

    public delegate string ThreadPoolRun(out int threadId);
    class Program
    {  

        static void Main(string[] args)
        {

            Console.WriteLine("開始測試線程池-委托。。。");
            int threadId = 0;
            ThreadPoolRun poolDele = RunThread;
            var t = new Thread(() => RunThread(out threadId));
            t.Start();
            t.Join();
            Console.WriteLine("線程ID {0} ",threadId);
            IAsyncResult r = poolDele.BeginInvoke(out threadId, Callback, "在線程池中同步調用回調函數");
            string result = poolDele.EndInvoke(out threadId, r);
            Console.WriteLine("線程池中工作線程ID :{0}", threadId);
            Console.WriteLine("返回結果:{0}",result); 

            Thread.Sleep(2000);
            Console.Read(); 

        }

        private static void  Callback(IAsyncResult r)
        {

            Console.WriteLine("開始調用回調函數。。。");
            Console.WriteLine("回調函數此時的狀態 :{0}",r.AsyncState);
            Console.WriteLine("調用此回調函數的線程是否在線程池 :{0}", Thread.CurrentThread.IsThreadPoolThread);
            Console.WriteLine("調用此回調函數的線程在線程池在的ID :{0}", Thread.CurrentThread.ManagedThreadId);
        }

        private static string RunThread(out int threadId)
        {
            Console.WriteLine("開始工作。。。");       

            Console.WriteLine("調用此回調函數的線程是否在線程池 :{0}", Thread.CurrentThread.IsThreadPoolThread);
            Thread.Sleep(TimeSpan.FromSeconds(2));
            threadId = Thread.CurrentThread.ManagedThreadId;
            return string.Format("此線程在線程池在的ID :{0}", threadId);
        }
    }
}

 

2.程序執行結果如下圖。

 

       上面的程序運行時,我們首先創建線程來執行委托操作,然后調用委托的BeginInvoke來執行回調方法,這個回調函數會在異步操作完成之后會被調用,并且會把一個自定義的值傳給這個回調函數,最后我們會得到一個實現了IAsyncResult接口的result對象,當線程池的工作線程在進行工作時,允許我們繼續其他操作。我們可以輪詢result對象的IsCompleted屬性,確定操作是否完成。也可以調用EndInvoke將IAsyncResult傳給委托參數。

 

二、     線程池中放入異步操作

 1.代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading; 

namespace ThreadPoolDemo
{ 
    class Program
    {   

        static void Main(string[] args)
        {
            Console.WriteLine("開始測試線程池-QueueUserWorkItem。。。");
            const int x = 1;
            const int y = 2;
            string workState = "工作狀態 2"; 

            ThreadPool.QueueUserWorkItem(AsyncOper);
            Thread.Sleep(1000);
            ThreadPool.QueueUserWorkItem(AsyncOper,"同步狀態");
            Thread.Sleep(1000);

            ThreadPool.QueueUserWorkItem(status => {
                Console.WriteLine("操作狀態 {0} ", status);
                Console.WriteLine("線程池中工作線程ID :{0}", Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(TimeSpan.FromSeconds(2));
            },"工作狀態");
 

            ThreadPool.QueueUserWorkItem(_ => {
                Console.WriteLine("操作結果x+y= {0} ,{1}", x+y,workState);
                Console.WriteLine("線程池中工作線程ID :{0}", Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(TimeSpan.FromSeconds(2));
            }, "工作狀態"); 

            Thread.Sleep(2000);
            Console.Read();
        }

        private static void  AsyncOper(object status)
        {
            Console.WriteLine("操作狀態 :{0} ",status??"null");

            Console.WriteLine("工作線程在線程池在的ID :{0}", Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(2000);
        }  

    }
}

 

2.執行結果如下。程序執行了兩次。

       程序首先定義了一個AsyncOper方法,然后使用QueueUserWorkItem將這個方法放入線程池中,然后再次放入一個AsyncOper方法,不過這次給方法調用傳一個對象。

       代碼中的調用Thread.sleep方法,是為了讓線程池中的工作線程為新操作重用。請注意打印出來的ThradId,如果ThreadID一樣則證明兩個操作重用了同一個工作線程。

 

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()