스레드 풀의 기술적 배경
객체 지향 프로그래밍에서 객체를 생성하고 소멸하는 데는 시간이 많이 소요됩니다. 객체를 생성하려면 메모리 자원이나 기타 자원을 확보해야 하므로 효율성 향상이 필요합니다. 서비스 프로그램의 한 가지 방법은 개체 생성 및 삭제 횟수를 최대한 줄이는 것입니다. 특히 일부 리소스 집약적 개체의 생성 및 삭제는 더욱 그렇습니다. 기존 객체를 활용하여 서비스를 제공하는 방법은 해결해야 할 핵심 문제입니다. 실제로 이것이 일부 "자원 공유" 기술이 등장한 이유입니다. 예를 들어, 익숙한 데이터베이스 연결 풀은 이 아이디어를 기반으로 만들어졌습니다. 이 기사에서 소개하는 스레드 풀 기술도 이 아이디어를 따릅니다.
스레드 풀 기술이 서버 프로그램의 성능을 향상시키는 방법
제가 언급한 서버 프로그램은 단순히 네트워크 요청을 받아들이는 프로그램이 아닌 클라이언트의 요청을 받아들이고 처리할 수 있는 프로그램을 말합니다. . 클라이언트가 요청한 웹 서버 프로그램입니다.
멀티 스레딩 기술은 주로 프로세서 유닛의 다중 스레드 실행 문제를 해결하며 프로세서 유닛의 유휴 시간을 크게 줄이고 프로세서 유닛의 처리 용량을 늘릴 수 있습니다. 하지만 멀티스레딩이 제대로 적용되지 않으면 단일 작업의 처리 시간이 늘어나게 됩니다. 간단한 예를 들 수 있습니다.
서버에서 작업을 완료하는 데 걸리는 시간이 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이 생성되는 기간을 조정할 뿐만 아니라 생성되는 스레드 수를 크게 줄여줍니다. 예를 보면:
서버가 하루에 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 중국어 홈페이지(www.php.cn)를 참고해주세요!