Heim > Artikel > Backend-Entwicklung > C# implementiert die Thread-Pool-Funktion selbst (1)
Technischer Hintergrund des Thread-Pools
Bei der objektorientierten Programmierung ist das Erstellen und Zerstören von Objekten sehr zeitaufwändig, da zum Erstellen eines Objekts Speicherressourcen oder andere Ressourcen benötigt werden und daher die Effizienz verbessert werden muss Eine Möglichkeit besteht darin, die Anzahl der Objekterstellung und -zerstörung so weit wie möglich zu reduzieren, insbesondere die Erstellung und Zerstörung einiger ressourcenintensiver Objekte. Die Verwendung vorhandener Objekte für die Bereitstellung ist ein zentrales Problem, das gelöst werden muss. Tatsächlich ist dies der Grund für die Entstehung einiger „Pooled Resource“-Technologien. Basierend auf dieser Idee wurde beispielsweise der bekannte Datenbankverbindungspool erstellt. Die in diesem Artikel vorgestellte Thread-Pool-Technologie entspricht dieser Idee.
Wie die Thread-Pool-Technologie die Leistung von Serverprogrammen verbessert
Das von mir erwähnte Serverprogramm bezieht sich auf ein Programm, das Clientanfragen annehmen und Anfragen verarbeiten kann, nicht nur solche, die Netzwerkanfragen akzeptieren . Das vom Client angeforderte Webserverprogramm.
Die Multithreading-Technologie löst hauptsächlich das Problem der Ausführung mehrerer Threads in der Prozessoreinheit. Sie kann die Leerlaufzeit der Prozessoreinheit erheblich reduzieren und die Durchsatzkapazität der Prozessoreinheit erhöhen. Wenn Multithreading jedoch nicht ordnungsgemäß angewendet wird, erhöht sich die Verarbeitungszeit einer einzelnen Aufgabe. Ein einfaches Beispiel kann gegeben werden:
Angenommen, die Zeit zum Erledigen einer Aufgabe auf einem Server beträgt T
T1 创建线程的时间 T2 在线程中执行任务的时间,包括线程间同步所需时间 T3 线程销毁的时间
Offensichtlich ist T = T1 + T2 + T3. Beachten Sie, dass dies eine extrem vereinfachte Annahme ist.
Es ist ersichtlich, dass T1 und T3 der durch Multithreading selbst verursachte Overhead sind. Wir sind bestrebt, die für T1 und T3 aufgewendete Zeit zu reduzieren und dadurch die Zeit von T zu verkürzen. Einige Thread-Benutzer bemerkten dies jedoch nicht, sodass im Programm häufig Threads erstellt oder zerstört wurden, was dazu führte, dass T1 und T3 einen erheblichen Teil von T belegten. Dies unterstreicht offensichtlich eher die Schwächen des Threads (T1, T3) als seine Stärken (Parallelität).
Die Thread-Pool-Technologie konzentriert sich darauf, die T1- und T3-Zeiten zu verkürzen oder anzupassen und dadurch die Leistung des Serverprogramms zu verbessern. Es ordnet T1 und T3 jeweils in den Start- und Endzeiträumen des Serverprogramms oder in einigen Leerlaufzeiträumen an, sodass bei der Verarbeitung von Kundenanfragen durch das Serverprogramm kein Overhead für T1 und T3 entsteht.
Der Thread-Pool passt nicht nur den Zeitraum an, in dem T1 und T3 generiert werden, sondern reduziert auch die Anzahl der erstellten Threads erheblich. Schauen Sie sich ein Beispiel an:
Angenommen, ein Server muss 50.000 Anfragen pro Tag verarbeiten, und für jede Anfrage ist ein separater Thread erforderlich. Wir vergleichen die Gesamtzahl der Threads, die von Servern generiert werden, die diese Anfragen mit und ohne Thread-Pool-Technologie verarbeiten. Im Thread-Pool ist die Anzahl der Threads im Allgemeinen festgelegt, sodass die Gesamtzahl der generierten Threads die Anzahl oder Obergrenze der Threads im Thread-Pool (im Folgenden als Thread-Pool-Größe bezeichnet) nicht überschreitet Verwenden Sie den Thread-Pool, um diese Anforderungen zu verarbeiten. Die Gesamtzahl der Threads beträgt 50.000. Die allgemeine Thread-Poolgröße beträgt deutlich weniger als 50.000. Daher verschwendet das Serverprogramm, das den Thread-Pool verwendet, keine Zeit mit der Verarbeitung von Anforderungen, um 50.000 zu erstellen, wodurch die Effizienz verbessert wird.
Dies sind alles Annahmen und können das Problem nicht vollständig erklären. Im Folgenden werde ich die einfache Implementierung des Thread-Pools diskutieren und einen Vergleichstest des Programms durchführen, um die Vorteile und Anwendungsbereiche der Thread-Technologie zu veranschaulichen.
Einfache Implementierung und Vergleichstest des Thread-Pools
Im Allgemeinen enthält ein einfacher Thread-Pool mindestens die folgenden Komponenten.
ThreadPoolManager: Wird zum Erstellen und Verwalten von Thread-Pools verwendet
Worker-Thread (WorkThread): Threads im Thread-Pool
Task-Schnittstelle (Task): jeweils Eine Schnittstelle, die eine Aufgabe enthält muss implementiert werden, damit der Arbeitsthread die Ausführung der Aufgabe planen kann.
Aufgabenwarteschlange: Wird zum Speichern unverarbeiteter Aufgaben verwendet. Stellen Sie einen Puffermechanismus bereit.
Als nächstes habe ich den einfachsten Thread-Pool demonstriert. Es wurde keine Optimierung durchgeführt.
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>
Worker-Thread-Klasse
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-Klasse und Implementierungsklasse
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); } } } }
Das Problem bei diesem einfachen Modell besteht darin, dass Oft versuchen wir ständig, die Aufgabe zu erhalten, wodurch der Leistungsabfall sehr gering ist. Die Methode, die verbessert werden muss, besteht darin, einen Semaphormechanismus hinzuzufügen, um zu verhindern, dass das Programm im Leerlauf ist!
Im nächsten Artikel werde ich es optimieren, um den Thread-Pool wirklich effizienter zu machen!
Das Obige ist der Inhalt der C#-eigenen Implementierung der Thread-Pool-Funktion (1). Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn)!