自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Nodejs線程池的設(shè)計(jì)與實(shí)現(xiàn)

開(kāi)發(fā) 架構(gòu)
本文介紹在nodejs線程模塊的基礎(chǔ)上,如何設(shè)計(jì)和實(shí)現(xiàn)一個(gè)線程池庫(kù)(https://github.com/theanarkh/nodejs-threadpool或npm i nodejs-threadpool )。下面是線程池的總體架構(gòu)。

[[347021]]

本文轉(zhuǎn)載自微信公眾號(hào)「編程雜技」,作者theanarkh。轉(zhuǎn)載本文請(qǐng)聯(lián)系編程雜技公眾號(hào)。 

前言:之前的版本不方便開(kāi)放,重新設(shè)計(jì)了一版nodejs的線程池庫(kù),本文介紹該庫(kù)的一些設(shè)計(jì)和實(shí)現(xiàn)。

nodejs雖然提供了線程的能力,但是很多時(shí)候,往往不能直接使用線程或者無(wú)限制地創(chuàng)建線程,比如我們有一個(gè)功能是cpu密集型的,如果一個(gè)請(qǐng)求就開(kāi)一個(gè)線程,這很明顯不是最好的實(shí)踐,這時(shí)候,我們需要使用池化的技術(shù),本文介紹在nodejs線程模塊的基礎(chǔ)上,如何設(shè)計(jì)和實(shí)現(xiàn)一個(gè)線程池庫(kù)(https://github.com/theanarkh/nodejs-threadpool或npm i nodejs-threadpool )。下面是線程池的總體架構(gòu)。

設(shè)計(jì)一個(gè)線程池,在真正寫(xiě)代碼之前,有很多設(shè)計(jì)需要考慮,大概如下:

1任務(wù)隊(duì)列的設(shè)計(jì),一個(gè)隊(duì)列,多個(gè)線程互斥訪問(wèn),或者每個(gè)線程一個(gè)隊(duì)列,不需要互斥訪問(wèn)。

2 線程退出的設(shè)計(jì),可以由主線程檢測(cè)空閑線程,然后使子線程退出。或者子線程退出,通知主線程??臻e不一定是沒(méi)有任務(wù)就退出,可以設(shè)計(jì)空閑時(shí)間達(dá)到閾值后退出,因?yàn)閯?chuàng)建線程是有時(shí)間開(kāi)銷(xiāo)的。

3 任務(wù)數(shù)的設(shè)計(jì),每個(gè)線程可以有個(gè)任務(wù)數(shù),還可以增加一個(gè)總?cè)蝿?wù)數(shù),即全部線程任務(wù)數(shù)加起來(lái)

4 選擇線程的設(shè)計(jì),選擇任務(wù)數(shù)最少的線程。

5 線程類(lèi)型的設(shè)計(jì),可以區(qū)分核心線程和預(yù)備線程,任務(wù)少的時(shí)候,核心線程處理就行。任務(wù)多也創(chuàng)建預(yù)備線程幫忙處理。

6 線程池類(lèi)型的設(shè)計(jì),cpu密集型的,線程數(shù)等于核數(shù),否則自定義線程數(shù)就行。

7 支持任務(wù)的取消和超時(shí)機(jī)制,防止一個(gè)任務(wù)時(shí)間過(guò)長(zhǎng)或者死循環(huán)。

本文介紹的線程池具體設(shè)計(jì)思想如下(參考java):

1 主線程維護(hù)一個(gè)隊(duì)列,子線程的任務(wù)由子線程負(fù)責(zé)分發(fā),不需要互斥訪問(wèn),子線程也不需要維護(hù)自己的隊(duì)列。

2 線程退出的設(shè)計(jì),主線程負(fù)責(zé)檢查子線程空閑時(shí)間是否達(dá)到閾值,是則使子線程退出。

3 任務(wù)數(shù)的設(shè)計(jì),主線程負(fù)責(zé)管理任務(wù)個(gè)數(shù)并應(yīng)有相應(yīng)的策略。

4 選擇線程的設(shè)計(jì),選擇任務(wù)數(shù)最少的線程。

5 線程類(lèi)型的設(shè)計(jì),區(qū)分核心線程和預(yù)備線程,任務(wù)少的時(shí)候,核心線程處理就行。任務(wù)多也創(chuàng)建預(yù)備線程幫忙處理。

6 線程池類(lèi)型的設(shè)計(jì),cpu密集型的,線程數(shù)等于核數(shù),否則自定義線程數(shù)就行。

7 支持任務(wù)的取消和超時(shí)機(jī)制,超時(shí)或者取消的時(shí)候,主線程判斷任務(wù)是待執(zhí)行還是正在執(zhí)行,如果是待執(zhí)行則從任務(wù)隊(duì)列中刪除,如果是正在執(zhí)行則殺死對(duì)應(yīng)的子線程。下面我們看一下具體的設(shè)計(jì)。

1 主線程和子線程通信的數(shù)據(jù)結(jié)構(gòu)

  1. // 任務(wù)類(lèi),一個(gè)任務(wù)對(duì)應(yīng)一個(gè)id 
  2. class Work { 
  3.     constructor({workId, filename, options}) { 
  4.         // 任務(wù)id 
  5.         this.workId = workId; 
  6.         // 任務(wù)邏輯,字符串或者js文件路徑 
  7.         this.filename = filename; 
  8.         // 任務(wù)返回的結(jié)果 
  9.         this.data = null
  10.         // 任務(wù)返回的錯(cuò)誤 
  11.         this.error = null
  12.         // 執(zhí)行任務(wù)時(shí)傳入的參數(shù),用戶定義 
  13.         this.options = options; 
  14.     } 

主線程給子線程分派一個(gè)任務(wù)的時(shí)候,就給子線程發(fā)送一個(gè)Work對(duì)象。在nodejs中線程間通信需要經(jīng)過(guò)序列化和反序列化,所以通信的數(shù)據(jù)結(jié)構(gòu)包括的信息不能過(guò)多。

2 子線程處理任務(wù)邏輯

  1. const { parentPort } = require('worker_threads'); 
  2. const vm = require('vm'); 
  3. const { isFunction, isJSFile } = require('./utils'); 
  4.  
  5. // 監(jiān)聽(tīng)主線程提交過(guò)來(lái)的任務(wù) 
  6. parentPort.on('message', async (work) => { 
  7.     try { 
  8.         const { filename, options } = work
  9.         let aFunction; 
  10.         if (isJSFile(filename)) { 
  11.             aFunction = require(filename); 
  12.         } else { 
  13.             aFunction = vm.runInThisContext(`(${filename})`); 
  14.         } 
  15.         if (!isFunction(aFunction)) { 
  16.             throw new Error('work type error: js file or string'); 
  17.         } 
  18.         work.data = await aFunction(options); 
  19.         parentPort.postMessage({event: 'done'work}); 
  20.     } catch (error) { 
  21.         work.error = error.toString(); 
  22.         parentPort.postMessage({event: 'error'work}); 
  23.     } 
  24. }); 
  25.  
  26. process.on('uncaughtException', (...rest) => { 
  27.     console.error(...rest); 
  28. }); 
  29.  
  30. process.on('unhandledRejection', (...rest) => { 
  31.     console.error(...rest); 
  32. }); 

子線程的邏輯比較簡(jiǎn)單,就是監(jiān)聽(tīng)主線程分派過(guò)來(lái)的任務(wù),然后執(zhí)行任務(wù),執(zhí)行完之后通知主線程。任務(wù)支持js文件和字符串代碼的形式。需要返回一個(gè)Promise或者async函數(shù)。用于用于通知主線程任務(wù)已經(jīng)完成。

3 線程池和業(yè)務(wù)的通信

  1. // 提供給用戶側(cè)的接口 
  2. class UserWork extends EventEmitter { 
  3.     constructor({ workId }) { 
  4.         super(); 
  5.         // 任務(wù)id 
  6.         this.workId = workId; 
  7.         // 支持超時(shí)取消任務(wù) 
  8.         this.timer = null
  9.         // 任務(wù)狀態(tài) 
  10.         this.state = WORK_STATE.PENDDING; 
  11.     } 
  12.     // 超時(shí)后取消任務(wù) 
  13.     setTimeout(timeout) { 
  14.         this.timer = setTimeout(() => { 
  15.             this.timer && this.cancel() && this.emit('timeout'); 
  16.         }, ~~timeout); 
  17.     } 
  18.     // 取消之前設(shè)置的定時(shí)器 
  19.     clearTimeout() { 
  20.         clearTimeout(this.timer); 
  21.         this.timer = null
  22.     } 
  23.     // 直接取消任務(wù),如果執(zhí)行完了就不能取消了,this.terminate是動(dòng)態(tài)設(shè)置的 
  24.     cancel() { 
  25.         if (this.state === WORK_STATE.END || this.state === WORK_STATE.CANCELED) { 
  26.            return false
  27.         } else { 
  28.             this.terminate(); 
  29.             return true
  30.         } 
  31.     } 
  32.     // 修改任務(wù)狀態(tài) 
  33.     setState(state) { 
  34.         this.state = state; 
  35.     } 

業(yè)務(wù)提交一個(gè)任務(wù)給線程池的時(shí)候,線程池會(huì)返回一個(gè)UserWork類(lèi),業(yè)務(wù)側(cè)通過(guò)UserWork類(lèi)和線程池通信。

4 管理子線程的數(shù)據(jù)結(jié)構(gòu)

  1. // 管理子線程的數(shù)據(jù)結(jié)構(gòu) 
  2. class Thread { 
  3.     constructor({ worker }) { 
  4.         // nodejs的Worker對(duì)象,nodejs的worker_threads模塊的Worker 
  5.         this.worker = worker; 
  6.         // 線程狀態(tài) 
  7.         this.state = THREAD_STATE.IDLE; 
  8.         // 上次工作的時(shí)間 
  9.         this.lastWorkTime = Date.now(); 
  10.     } 
  11.     // 修改線程狀態(tài) 
  12.     setState(state) { 
  13.         this.state = state; 
  14.     } 
  15.     // 修改線程最后工作時(shí)間 
  16.     setLastWorkTime(time) { 
  17.         this.lastWorkTime = time
  18.     } 

線程池中維護(hù)了多個(gè)子線程,Thread類(lèi)用于管理子線程的信息。

5 線程池 線程池的實(shí)現(xiàn)是核心,我們分為幾個(gè)部分講。

5.1 支持的配置

  1. constructor(options = {}) { 
  2.         this.options = options; 
  3.         // 子線程隊(duì)列 
  4.         this.workerQueue = []; 
  5.         // 核心線程數(shù) 
  6.         this.coreThreads = ~~options.coreThreads || config.CORE_THREADS; 
  7.         // 線程池最大線程數(shù),如果不支持動(dòng)態(tài)擴(kuò)容則最大線程數(shù)等于核心線程數(shù) 
  8.         this.maxThreads = options.expansion !== false ? Math.max(this.coreThreads, config.MAX_THREADS) : this.coreThreads; 
  9.         // 超過(guò)任務(wù)隊(duì)列長(zhǎng)度時(shí)的處理策略 
  10.         this.discardPolicy = options.discardPolicy ? options.discardPolicy : DISCARD_POLICY.NOT_DISCARD; 
  11.         // 是否預(yù)創(chuàng)建子線程 
  12.         this.preCreate = options.preCreate === true
  13.         // 線程最大空閑時(shí)間,達(dá)到后自動(dòng)退出 
  14.         this.maxIdleTime = ~~options.maxIdleTime || config.MAX_IDLE_TIME; 
  15.         // 是否預(yù)創(chuàng)建線程池 
  16.         this.preCreate && this.preCreateThreads(); 
  17.         // 保存線程池中任務(wù)對(duì)應(yīng)的UserWork 
  18.         this.workPool = {}; 
  19.         // 線程池中當(dāng)前可用的任務(wù)id,每次有新任務(wù)時(shí)自增1 
  20.         this.workId = 0; 
  21.         // 線程池中的任務(wù)隊(duì)列 
  22.         this.queue = []; 
  23.         // 線程池總?cè)蝿?wù)數(shù) 
  24.         this.totalWork = 0; 
  25.         // 支持的最大任務(wù)數(shù) 
  26.         this.maxWork = ~~options.maxWork || config.MAX_WORK; 
  27.         // 處理任務(wù)的超時(shí)時(shí)間,全局配置 
  28.         this.timeout = ~~options.timeout; 
  29.         this.pollIdle(); 
  30.     } 

上面的代碼列出了線程池所支持的能力。

5.2 創(chuàng)建線程

  1. newThread() { 
  2.         const worker = new Worker(workerPath); 
  3.         const thread = new Thread({worker}); 
  4.         this.workerQueue.push(thread); 
  5.         const threadId = worker.threadId; 
  6.         worker.on('exit', () => { 
  7.             // 找到該線程對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu),然后刪除該線程的數(shù)據(jù)結(jié)構(gòu) 
  8.             const position = this.workerQueue.findIndex(({worker}) => { 
  9.                 return worker.threadId === threadId; 
  10.             }); 
  11.             const exitedThread = this.workerQueue.splice(position, 1); 
  12.             // 退出時(shí)狀態(tài)是BUSY說(shuō)明還在處理任務(wù)(非正常退出) 
  13.             this.totalWork -= exitedThread.state === THREAD_STATE.BUSY ? 1 : 0; 
  14.         }); 
  15.         // 和子線程通信 
  16.         worker.on('message', (result) => { 
  17.             const { 
  18.                 work
  19.                 event, 
  20.             } = result; 
  21.             const { data, error, workId } = work
  22.             // 通過(guò)workId拿到對(duì)應(yīng)的userWork 
  23.             const userWork = this.workPool[workId]; 
  24.             // 不存在說(shuō)明任務(wù)被取消了 
  25.             if (!userWork) { 
  26.                 return
  27.             } 
  28.             // 修改線程池?cái)?shù)據(jù)結(jié)構(gòu) 
  29.             this.endWork(userWork); 
  30.  
  31.             // 修改線程數(shù)據(jù)結(jié)構(gòu) 
  32.             thread.setLastWorkTime(Date.now()); 
  33.  
  34.             // 還有任務(wù)則通知子線程處理,否則修改子線程狀態(tài)為空閑 
  35.             if (this.queue.length) { 
  36.                 // 從任務(wù)隊(duì)列拿到一個(gè)任務(wù)交給子線程 
  37.                 this.submitWorkToThread(thread, this.queue.shift()); 
  38.             } else { 
  39.                 thread.setState(THREAD_STATE.IDLE); 
  40.             } 
  41.  
  42.             switch(event) { 
  43.                 case 'done'
  44.                     // 通知用戶,任務(wù)完成 
  45.                     userWork.emit('done', data); 
  46.                     break; 
  47.                 case 'error'
  48.                     // 通知用戶,任務(wù)出錯(cuò) 
  49.                     if (EventEmitter.listenerCount(userWork, 'error')) { 
  50.                         userWork.emit('error', error); 
  51.                     } 
  52.                     break; 
  53.                 default: break; 
  54.             } 
  55.         }); 
  56.         worker.on('error', (...rest) => { 
  57.             console.error(...rest); 
  58.         }); 
  59.         return thread; 
  60.     } 

創(chuàng)建線程,并保持線程對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)、退出、通信管理、任務(wù)分派。子線程執(zhí)行完任務(wù)后,會(huì)通知線程池,主線程通知用戶。

5.3 選擇線程

  1. selectThead() { 
  2.         // 找出空閑的線程,把任務(wù)交給他 
  3.         for (let i = 0; i < this.workerQueue.length; i++) { 
  4.             if (this.workerQueue[i].state === THREAD_STATE.IDLE) { 
  5.                 return this.workerQueue[i]; 
  6.             } 
  7.         } 
  8.         // 沒(méi)有空閑的則隨機(jī)選擇一個(gè) 
  9.         return this.workerQueue[~~(Math.random() * this.workerQueue.length)]; 
  10.     } 

當(dāng)用戶給線程池提交一個(gè)任務(wù)時(shí),線程池會(huì)選擇一個(gè)空閑的線程處理該任務(wù)。如果沒(méi)有可用線程則任務(wù)插入待處理隊(duì)列等待處理。

5.4 提交任務(wù)

  1. // 給線程池提交一個(gè)任務(wù) 
  2.     submit(filename, options = {}) { 
  3.         return new Promise(async (resolve, reject) => { 
  4.             let thread; 
  5.             // 沒(méi)有線程則創(chuàng)建一個(gè) 
  6.             if (this.workerQueue.length) { 
  7.                 thread = this.selectThead(); 
  8.                 // 該線程還有任務(wù)需要處理 
  9.                 if (thread.state === THREAD_STATE.BUSY) { 
  10.                     // 子線程個(gè)數(shù)還沒(méi)有達(dá)到核心線程數(shù),則新建線程處理 
  11.                     if (this.workerQueue.length < this.coreThreads) { 
  12.                         thread = this.newThread(); 
  13.                     } else if (this.totalWork + 1 > this.maxWork){ 
  14.                         // 總?cè)蝿?wù)數(shù)已達(dá)到閾值,還沒(méi)有達(dá)到線程數(shù)閾值,則創(chuàng)建 
  15.                         if(this.workerQueue.length < this.maxThreads) { 
  16.                             thread = this.newThread(); 
  17.                         } else { 
  18.                             // 處理溢出的任務(wù) 
  19.                             switch(this.discardPolicy) { 
  20.                                 case DISCARD_POLICY.ABORT:  
  21.                                     return reject(new Error('queue overflow')); 
  22.                                 case DISCARD_POLICY.CALLER_RUN: 
  23.                                     const workId = this.generateWorkId(); 
  24.                                     const userWork =  new UserWork({workId});  
  25.                                     userWork.setState(WORK_STATE.RUNNING); 
  26.                                     userWork.terminate = () => { 
  27.                                         userWork.setState(WORK_STATE.CANCELED); 
  28.                                     }; 
  29.                                     this.timeout && userWork.setTimeout(this.timeout); 
  30.                                     resolve(userWork); 
  31.                                     try { 
  32.                                         let aFunction; 
  33.                                         if (isJSFile(filename)) { 
  34.                                             aFunction = require(filename); 
  35.                                         } else { 
  36.                                             aFunction = vm.runInThisContext(`(${filename})`); 
  37.                                         } 
  38.                                         if (!isFunction(aFunction)) { 
  39.                                             throw new Error('work type error: js file or string'); 
  40.                                         } 
  41.                                         const result = await aFunction(options); 
  42.                                         // 延遲通知,讓用戶有機(jī)會(huì)取消或者注冊(cè)事件 
  43.                                         setImmediate(() => { 
  44.                                             if (userWork.state !== WORK_STATE.CANCELED) { 
  45.                                                 userWork.setState(WORK_STATE.END); 
  46.                                                 userWork.emit('done', result); 
  47.                                             } 
  48.                                         }); 
  49.                                     } catch (error) { 
  50.                                         setImmediate(() => { 
  51.                                             if (userWork.state !== WORK_STATE.CANCELED) { 
  52.                                                 userWork.setState(WORK_STATE.END); 
  53.                                                 userWork.emit('error', error.toString()); 
  54.                                             } 
  55.                                         }); 
  56.                                     } 
  57.                                     return
  58.                                 case DISCARD_POLICY.OLDEST_DISCARD:  
  59.                                     const work = this.queue.shift(); 
  60.                                     // maxWork為1時(shí),work會(huì)為空 
  61.                                     if (work && this.workPool[work.workId]) { 
  62.                                         this.cancelWork(this.workPool[work.workId]); 
  63.                                     } else { 
  64.                                         return reject(new Error('no work can be discarded')); 
  65.                                     } 
  66.                                     break; 
  67.                                 case DISCARD_POLICY.DISCARD: 
  68.                                     return reject(new Error('discard')); 
  69.                                 case DISCARD_POLICY.NOT_DISCARD: 
  70.                                     break; 
  71.                                 default:  
  72.                                     break; 
  73.                             } 
  74.                         } 
  75.                     } 
  76.                 } 
  77.             } else { 
  78.                 thread = this.newThread(); 
  79.             } 
  80.             // 生成一個(gè)任務(wù)id 
  81.             const workId = this.generateWorkId(); 
  82.  
  83.             // 新建一個(gè)UserWork 
  84.             const userWork =  new UserWork({workId});  
  85.             this.timeout && userWork.setTimeout(this.timeout); 
  86.  
  87.             // 新建一個(gè)work 
  88.             const work = new Work({ workId, filename, options }); 
  89.  
  90.             // 修改線程池?cái)?shù)據(jù)結(jié)構(gòu),把UserWork和Work關(guān)聯(lián)起來(lái) 
  91.             this.addWork(userWork); 
  92.  
  93.             // 選中的線程正在處理任務(wù),則先緩存到任務(wù)隊(duì)列 
  94.             if (thread.state === THREAD_STATE.BUSY) { 
  95.                 this.queue.push(work); 
  96.                 userWork.terminate = () => { 
  97.                     this.cancelWork(userWork); 
  98.                     this.queue = this.queue.filter((node) => { 
  99.                         return node.workId !== work.workId; 
  100.                     }); 
  101.                 } 
  102.             } else { 
  103.                 this.submitWorkToThread(thread, work); 
  104.             } 
  105.  
  106.             resolve(userWork); 
  107.         }) 
  108.     } 
  109.  
  110.     submitWorkToThread(thread, work) { 
  111.         const userWork = this.workPool[work.workId]; 
  112.         userWork.setState(WORK_STATE.RUNNING); 
  113.         // 否則交給線程處理,并修改狀態(tài)和記錄該線程當(dāng)前處理的任務(wù)id 
  114.         thread.setState(THREAD_STATE.BUSY); 
  115.         thread.worker.postMessage(work); 
  116.         userWork.terminate = () => { 
  117.             this.cancelWork(userWork); 
  118.             thread.setState(THREAD_STATE.DEAD); 
  119.             thread.worker.terminate(); 
  120.         } 
  121.     } 
  122.  
  123.     addWork(userWork) { 
  124.         userWork.setState(WORK_STATE.PENDDING); 
  125.         this.workPool[userWork.workId] = userWork; 
  126.         this.totalWork++; 
  127.     } 
  128.  
  129.     endWork(userWork) { 
  130.         delete this.workPool[userWork.workId]; 
  131.         this.totalWork--; 
  132.         userWork.setState(WORK_STATE.END); 
  133.         userWork.clearTimeout();  
  134.     } 
  135.  
  136.     cancelWork(userWork) { 
  137.         delete this.workPool[userWork.workId]; 
  138.         this.totalWork--; 
  139.         userWork.setState(WORK_STATE.CANCELED); 
  140.         userWork.emit('cancel'); 
  141.     } 

提交任務(wù)是線程池暴露給用戶側(cè)的接口,主要處理的邏輯包括,根據(jù)當(dāng)前的策略判斷是否需要新建線程、選擇線程處理任務(wù)、排隊(duì)任務(wù)等,如果任務(wù)數(shù)達(dá)到閾值,則根據(jù)丟棄策略處理該任務(wù)。

5.5 空閑處理

  1. pollIdle() { 
  2.         setTimeout(() => { 
  3.             for (let i = 0; i < this.workerQueue.length; i++) { 
  4.                 const node = this.workerQueue[i]; 
  5.                 if (node.state === THREAD_STATE.IDLE && Date.now() - node.lastWorkTime > this.maxIdleTime) { 
  6.                     node.worker.terminate(); 
  7.                 } 
  8.             } 
  9.             this.pollIdle(); 
  10.         }, 1000); 
  11.     } 

當(dāng)子線程空閑時(shí)間達(dá)到閾值后,主線程會(huì)殺死子線程,避免浪費(fèi)系統(tǒng)資源??偨Y(jié),這就是線程池具體的設(shè)計(jì)和實(shí)現(xiàn),另外創(chuàng)建線程失敗會(huì)導(dǎo)致主線程掛掉,所以使用線程的時(shí)候,最后新開(kāi)一個(gè)子進(jìn)程來(lái)管理該線程池。

 

責(zé)任編輯:武曉燕 來(lái)源: 編程雜技
相關(guān)推薦

2020-10-23 08:31:15

Nodejs-Ipc設(shè)計(jì)實(shí)現(xiàn)

2024-11-06 09:39:52

2017-05-04 16:33:58

Java線程池實(shí)踐

2020-12-10 08:24:40

線程池線程方法

2024-07-15 08:20:24

2013-05-23 15:59:00

線程池

2009-07-22 09:39:18

CLR線程池

2012-02-01 11:20:23

Java線程

2020-03-05 15:34:16

線程池C語(yǔ)言局域網(wǎng)

2018-04-27 10:35:08

Tomcat連接數(shù)線程池

2012-05-15 02:18:31

Java線程池

2018-10-31 15:54:47

Java線程池源碼

2021-06-06 23:40:53

線程池使用場(chǎng)景

2025-02-24 00:00:10

.NET線程池模型

2024-06-04 07:52:04

2024-05-06 00:00:00

ThreadPool線程調(diào)度

2023-08-02 08:03:08

Python線程池

2021-05-26 11:30:24

Java線程池代碼

2020-08-20 07:54:58

Node多線程解密

2023-05-19 08:01:24

Key消費(fèi)場(chǎng)景
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)