c#自己實作執行緒池功能(一)

黄舟
發布: 2016-12-27 14:36:06
原創
1872 人瀏覽過

線程池的技術背景

在物件導向程式設計中,創建和銷毀物件是很費時間的,因為創建一個物件要獲取記憶體資源或者其它更多資源,所以提高服務程式效率的一個手段就是盡可能減少創建和銷毀物件的次數,特別是一些很耗資源的物件創建和銷毀。如何利用已有物件來服務就是一個需要解決的關鍵問題,其實這就是一些"池化資源"技術產生的原因。例如大家所熟悉的資料庫連結池正是遵循這個想法而產生的,本文將介紹的線程池技術同樣符合這個想法。

線程池技術如何提高伺服器程式的效能

我所提到伺服器程式是指能夠接受客戶請求並能處理請求的程序,而不只是指那些接受網路客戶請求的網路伺服器程式。

多執行緒技術主要解決處理器單元內多個執行緒執行的問題,它可以顯著減少處理器單元的閒置時間,增加處理器單元的吞吐能力。但如果對多執行緒應用不當,會增加單一任務的處理時間。可以舉一個簡單的例子:

假設在一台伺服器完成一項任務的時間為T

T1 创建线程的时间
 T2 在线程中执行任务的时间,包括线程间同步所需时间
 T3 线程销毁的时间
登入後複製

顯然T = T1+T2+T3。注意這是一個極度簡化的假設。

可以看出T1,T3是多執行緒本身的帶來的開銷,我們渴望減少T1,T3所花費的時間,從而減少T的時間。但一些線程的使用者並沒有註意到這一點,所以在程式中頻繁的創建或銷毀線程,這導致T1和T3在T中佔有相當比例。顯然這是突顯了線程的弱點(T1,T3),而不是優點(並發性)。

線程池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高伺服器程式效能的。它把T1,T3分別安排在伺服器程式的啟動和結束的時間段或一些空閒的時間段,這樣在伺服器程式處理客戶請求時,不會有T1,T3的開銷了。

執行緒池不僅調整T1,T3產生的時間段,而且它還顯著減少了創建執行緒的數目。在看一個例子:

假設一個伺服器一天要處理50000個請求,並且每個請求需要一個單獨的執行緒完成。我們比較利用線程池技術和不利於線程池技術的伺服器處理這些請求時所產生的總線程數。在線程池中,線程數一般是固定的,所以產生線程總數不會超過線程池中線程的數目或者上限(以下簡稱線程池尺寸),而如果伺服器不利用線程池來處理這些請求則線程總數為50000。一般線程池尺寸是遠小於50000。所以利用線程池的伺服器程式不會為了創建50000而在處理請求時浪費時間,從而提高效率。

這些都是假設,不能充分說明問題,下面我將討論線程池的簡單實現並對該程序進行對比測試,以說明線程技術優點及應用領域。

執行緒池的簡單實作及對比測試

一般一個簡單執行緒池至少包含下列組成部分。

執行緒池管理器(ThreadPoolManager):用於建立並管理執行緒池

工作執行緒(WorkThread): 執行緒池中執行緒

任務介面(Task):每個任務必須實現的接口,以供工作執行緒調度任務的執行。

任務佇列:用於存放沒有處理的任務。提供一種緩衝機制。 
接下來我示範了一個 最簡單的執行緒池。沒有進行任何優化的。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
 
namespace ThreadManager
{
    public class ThreadPoolManager
    {
        private int MaxThreadNum;
        private int MinThreadNum;
        private int GrowStepNum;
        //线程数量
        public int ThreadNum{get;set;}
        //默认线程数量
        public int DefaultThreadNum { get; set; }
 
        private Queue<task> TaskQueue;
        private Queue<workthread> WorkThreadList;
 
        public ThreadPoolManager(int i)
        {
            TaskQueue = new Queue<task>();
            WorkThreadList = new Queue<workthread>();
            DefaultThreadNum = 10;
            if (i > 0)
                DefaultThreadNum = i;
            CreateThreadPool(i);
        }
        public ThreadPoolManager():this(10)
        {
        }
        public bool IsAllTaskFinish()
        {
            return TaskQueue.Count == 0;
        }
        public void CreateThreadPool(int i)
        {
            if (WorkThreadList == null)
                WorkThreadList = new Queue<workthread>();
            lock (WorkThreadList)
            {
                for (int j = 0; j < i;j++)
                {
                    ThreadNum++;
                    WorkThread workthread = new WorkThread(ref TaskQueue,ThreadNum);
                    WorkThreadList.Enqueue(workthread);
                }
            }
        }
        public void AddTask(Task task)
        {
            
            if (task == null)
                return;
            lock (TaskQueue)
            {
                TaskQueue.Enqueue(task);
            }
            //Monitor.Enter(TaskQueue);
            //TaskQueue.Enqueue(task);
            //Monitor.Exit(TaskQueue);
        }
        public void CloseThread()
        {
            //Object obj = null;
            while (WorkThreadList.Count != 0)
            {
                try
                {
                    WorkThread workthread = WorkThreadList.Dequeue();
                    workthread.CloseThread();
                    continue;
                }
                catch (Exception)
                {
                }
                break;
            }
        }
    }
}
</workthread></workthread></task></workthread></task>
登入後複製

工作線程類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
 
namespace ThreadManager
{
    public class WorkThread
    {
        public int ThreadNum { get; set; }
        private bool flag;
        private Queue<task> TaskQueue;
        private Task task;
        public WorkThread(ref Queue<task> queue, int i)
        {
            this.TaskQueue = queue;
            ThreadNum = i;
            flag = true;
            new Thread(run).Start();
        }
        public void run()
        {
            while (flag && TaskQueue != null)
            {
                //获取任务
                lock (TaskQueue)
                {
                    try
                    {
                            task = TaskQueue.Dequeue();
                    }
                    catch (Exception)
                    {
                        task = null;
                    }
                    if (task == null)
                        continue;
                }
                try
                {
                    task.SetEnd(false);
                    task.StartTask();
                }
                catch (Exception)
                {
                }
                try
                {
                    if (!task.IsEnd())
                    {
                        task.SetEnd(false);
                        task.EndTask();
                    }
                }
                catch (Exception)
                {
                }
 
            }//end of while
        }
        public void CloseThread()
        {
            flag = false;
            try
            {
                if (task != null)
                    task.EndTask();
            }
            catch (Exception)
            {   
            }
        }
    }
}</task></task>
登入後複製

task類和實現類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace ThreadManager
{
    public interface Task
    {
        /// <summary>
        /// set flag of task.
        /// </summary>
        void SetEnd(bool flag);
        /// <summary>
        /// start task.
        /// </summary>
        void StartTask();
        /// <summary>
        /// end task.
        /// </summary>
        void EndTask();
        /// <summary>
        /// get status of task.
        /// </summary>
        /// <returns></returns>
        bool IsEnd();
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
 
namespace ThreadManager
{
    public class TestTask:Task
    {
        private bool is_end;
        public void SetEnd(bool flag)
        {
            is_end = flag;
        }
        public void StartTask()
        {
            Run();
        }
        public void EndTask()
        {
            is_end = true;
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId + ":"+"结束!");
        }
        public bool IsEnd()
        {
            return is_end;
        }
        public void Run()
        {
            for (int i = 0; i < 1000; i++)
            {
                Console.WriteLine(Thread.CurrentThread.ManagedThreadId+":"+i);
            }
        }
 
    }
}
登入後複製

這個簡單的模型存在的問題是,很多時候獲取TASK都是在不斷的嘗試,使得性能降的很低,需要改進的方法是增加一個信號量的機制,不讓程式空轉!

在下一篇文章中我會進行最佳化,使得線程池真正的提高效率! 

 以上就是c#自己實作執行緒池功能(一)的內容,更多相關內容請關注PHP中文網(www.php.cn)!


相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!