スレッド プールの技術的背景
オブジェクト指向プログラミングでは、オブジェクトの作成と破棄には非常に時間がかかります。オブジェクトの作成にはメモリ リソースやその他のリソースを取得する必要があるためです。そのため、サービス プログラムの効率を向上させる 1 つの方法は、サービス プログラムの効率を向上させることです。可能な限り多くのオブジェクトの数 オブジェクトの作成と破棄の回数、特にリソースを大量に消費するオブジェクトの作成と破棄。既存のオブジェクトをどのように使用してサービスを提供するかは、解決する必要がある重要な問題です。実際、これがいくつかの「プールされたリソース」テクノロジーの出現の理由です。たとえば、おなじみのデータベース接続プールはこの考えに基づいて作成されました。この記事で紹介するスレッド プール テクノロジもこの考えに準拠しています。
スレッド プール テクノロジーがサーバー プログラムのパフォーマンスをどのように向上させるか
私が述べたサーバー プログラムとは、ネットワーク クライアントの要求を受け入れるネットワーク サーバー プログラムだけを指すのではなく、クライアントの要求を受け入れて要求を処理できるプログラムを指します。
マルチスレッド技術は主にプロセッサユニット内でのマルチスレッド実行の問題を解決し、プロセッサユニットのアイドル時間を大幅に短縮し、プロセッサユニットのスループット容量を向上させることができます。ただし、マルチスレッドが適切に適用されていない場合、単一タスクの処理時間が増加します。簡単な例を挙げることができます:
サーバー上のタスクを完了する時間が 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 が生成される期間を調整するだけでなく、作成されるスレッドの数も大幅に削減します。例を見てみましょう:
サーバーが 1 日に 50,000 件のリクエストを処理する必要があり、各リクエストを完了するために別のスレッドが必要だとします。スレッド プール テクノロジを使用した場合と使用しない場合で、これらのリクエストを処理するサーバーによって生成されたスレッドの総数を比較します。スレッド プールでは、一般にスレッドの数が固定されているため、サーバーが制限しない場合、生成されるスレッドの総数がスレッド プール内のスレッドの数または上限 (以下、スレッド プール サイズと呼びます) を超えることはありません。スレッド プールを使用してこれらのリクエストを処理すると、スレッドの総数は 50,000 になります。一般的なスレッド プール サイズは 50,000 よりもはるかに小さいです。したがって、スレッド プールを使用するサーバー プログラムは、50,000 個を作成するためにリクエストの処理に時間を費やすことがなくなり、効率が向上します。
これらは仮定であり、問題を完全に説明することはできません。以下では、スレッド プールの簡単な実装について説明し、プログラムの比較テストを実行して、スレッド テクノロジの利点と応用分野を説明します。
スレッドプールの簡単な実装と比較テスト
一般に、単純なスレッドプールには少なくとも次のコンポーネントが含まれています。
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>
タスククラスと実装クラス
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# 独自のスレッド プール関数の実装 (1) の内容です。さらに関連する内容については、PHP 中国語 Web サイト (www.php.cn) を参照してください。