Contexte technique du pool de threads
Dans la programmation orientée objet, la création et la destruction d'objets prennent beaucoup de temps, car la création d'un objet nécessite l'obtention de ressources mémoire ou d'autres ressources, il est donc nécessaire d'améliorer l'efficacité du programme de service. Une solution consiste à réduire autant que possible le nombre de créations et de destructions d'objets, en particulier la création et la destruction de certains objets gourmands en ressources. Comment utiliser les objets existants pour servir est une question clé qui doit être résolue. En fait, c'est la raison de l'émergence de certaines technologies de « ressources mutualisées ». Par exemple, le pool de connexions à la base de données familier a été créé sur la base de cette idée. La technologie de pool de threads présentée dans cet article est également conforme à cette idée.
Comment la technologie des pools de threads améliore les performances des programmes serveur
Le programme serveur que j'ai mentionné fait référence à un programme qui peut accepter les demandes des clients et traiter les demandes, pas seulement celles qui acceptent les demandes réseau . Le programme du serveur Web demandé par le client.
La technologie multi-thread résout principalement le problème de l'exécution de plusieurs threads dans l'unité de processeur. Elle peut réduire considérablement le temps d'inactivité de l'unité de processeur et augmenter la capacité de débit de l'unité de processeur. Cependant, si le multithreading n’est pas appliqué correctement, cela augmentera le temps de traitement d’une seule tâche. Un exemple simple peut être donné :
Supposons que le temps pour terminer une tâche sur un serveur soit T
T1 创建线程的时间 T2 在线程中执行任务的时间,包括线程间同步所需时间 T3 线程销毁的时间
Évidemment T = T1 + T2 + T3. Notez qu’il s’agit d’une hypothèse extrêmement simplifiée.
On peut voir que T1 et T3 sont les frais généraux causés par le multi-thread lui-même. Nous sommes impatients de réduire le temps passé sur T1 et T3, réduisant ainsi le temps de T. Cependant, certains utilisateurs de threads ne le remarquent pas, c'est pourquoi des threads sont fréquemment créés ou détruits dans le programme, ce qui fait que T1 et T3 occupent une proportion considérable de T. Évidemment, cela met en évidence les faiblesses du thread (T1, T3) plutôt que ses points forts (concurrence).
La technologie des pools de threads se concentre sur la manière de raccourcir ou d'ajuster les temps T1 et T3, améliorant ainsi les performances du programme serveur. Il organise T1 et T3 respectivement dans les périodes de démarrage et de fin du programme serveur ou dans certaines périodes d'inactivité, de sorte que lorsque le programme serveur traite les demandes des clients, il n'y aura pas de surcharge de T1 et T3.
Le pool de threads ajuste non seulement la période de temps pendant laquelle T1 et T3 sont générés, mais il réduit également considérablement le nombre de threads créés. Prenons un exemple :
Supposons qu'un serveur doive traiter 50 000 requêtes par jour et que chaque requête nécessite un thread distinct pour être complétée. Nous comparons le nombre total de threads générés par un serveur qui gère ces requêtes avec et sans la technologie de pool de threads. Dans le pool de threads, le nombre de threads est généralement fixe, de sorte que le nombre total de threads générés ne dépassera pas le nombre ou la limite supérieure de threads dans le pool de threads (ci-après dénommé la taille du pool de threads si le serveur ne le fait pas). utilisez le pool de threads pour traiter ces requêtes, le nombre total de threads sera de 50 000. La taille générale du pool de threads est bien inférieure à 50 000. Par conséquent, le programme serveur qui utilise le pool de threads ne perdra pas de temps à traiter les requêtes afin d'en créer 50 000, améliorant ainsi l'efficacité.
Ce ne sont que des hypothèses et ne peuvent pas expliquer complètement le problème. Ci-dessous, je discuterai de la mise en œuvre simple du pool de threads et effectuerai un test comparatif sur le programme pour illustrer les avantages et les domaines d'application de la technologie des threads.
Implémentation simple et test de comparaison du pool de threads
Généralement, un simple pool de threads contient au moins les composants suivants.
ThreadPoolManager : utilisé pour créer et gérer des pools de threads
Worker thread (WorkThread) : threads dans le pool de threads
Interface de tâches (Task) : chacun Une interface qu'une tâche doit être implémenté pour que le thread de travail puisse planifier l'exécution de la tâche.
File d'attente des tâches : utilisée pour stocker les tâches non traitées. Fournir un mécanisme tampon.
Ensuite, j'ai démontré le pool de threads le plus simple. Aucune optimisation n'a été effectuée.
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>
Classe de thread de travail
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>
Classe de tâches et classe d'implémentation
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); } } } }
Le problème avec ce modèle simple est que, Plusieurs fois, nous essayons constamment d'obtenir la TASK, ce qui rend les performances très faibles. La méthode qui doit être améliorée est d'ajouter un mécanisme de sémaphore pour empêcher le programme de tourner au ralenti !
Dans le prochain article, je vais l'optimiser pour rendre le pool de threads vraiment plus efficace !
Ce qui précède est le contenu de la propre implémentation en C# de la fonction de pool de threads (1). Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www.php.cn) !