在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

線程池的基本概念

科技綠洲 ? 來源:Linux開發架構之路 ? 作者:Linux開發架構之路 ? 2023-11-10 16:37 ? 次閱讀

線程池的基本概念

不管線程池是什么東西!但是我們必須知道線程池被搞出來的目的就是:提高程序執行效率而設計出來的;

了解了線程池的目的后:我們就可以開始理解線程池:

首先回答一個問題:為什么會有線程池?

呃呃,我這么問就很奇怪,因為線程池是什么我都沒說,怎么會知道為什么會有線程池呢?所以我打算帶大家去思考一個場景:

當我們的程序中:有一批任務到來時候(通常該任務都是從網絡來的),我們就會創建一堆線程去處理這一批任務;

雖然說創建線程的成本開銷并不大,但是這里有個問題:當我們任務來到時候,你才去創建線程去處理這個任務,你不覺得這樣很慢嗎?

是否我們可以換個思路:假如我們有一種手段:使得任務一到來,就可以馬上有線程去處理這批任務,這樣是不是相對于前面等線程來到,再創建線程去處理時候快得多;

所以說:線程池就是基于上面的思路設計的;線程池就是:預先創建好一大批線程,同時線程池維護一個隊列,來存放到來的任務,當隊列中一旦有任務時候,預先創建好的一大批線程就可以并發處理這一批任務了;

我們抽象出一個模型:

任務派發者是誰? 是生產者;

任務存儲的隊列是什么?是一個容器,數組,鏈表,只要是可以存放產品(數據)的東西即可;

拿任務去處理的是誰?是消費者;

所以說:線程池本質就是一個生產者消費者的模型;

而我們線程池只需要關注兩個點:一個存放任務的隊列,和消費隊列任務的消費者即可;而生產者暫時不用關注,因為生產者是你外部搞出任務丟給線程池去使用;那么什么時候可以關心生產者呢?

也就是當我們去使用線程池的時候咯;這不就是妥妥的生產者消費者模型嘛!

圖片

線程池實現的基本思路:

在各個編程語言的語種中都有線程池的概念,并且很多語言中直接提供了線程池,作為程序猿直接使用就可以了,下面給大家介紹一下線程池的實現原理:

線程池的組成主要分為 3 個部分,這三部分配合工作就可以得到一個完整的線程池:

任務隊列,存儲需要處理的任務,由工作的線程來處理這些任務

通過線程池提供的 API 函數,將一個待處理的任務添加到任務隊列,或者從任務隊列中刪除;

已處理的任務會被從任務隊列中刪除;

線程池的使用者,也就是調用線程池函數往任務隊列中添加任務的線程就是生產者線程;

工作的線程(任務隊列任務的消費者) ,N個

線程池中維護了一定數量的工作線程,他們的作用是是不停的讀任務隊列,從里邊取出任務并處理

工作的線程相當于是任務隊列的消費者角色;

如果任務隊列為空,工作的線程將會被阻塞 (使用條件變量 / 信號量阻塞);

如果阻塞之后有了新的任務,由生產者將阻塞解除,工作線程開始工作;

管理者線程(不處理任務隊列中的任務),1個

它的任務是周期性的對任務隊列中的任務數量以及處于忙狀態的工作線程個數進行檢測;

當任務過多的時候,可以適當的創建一些新的工作線程;

當任務過少的時候,可以適當的銷毀一些工作的線程;

線程池的代碼

1.任務隊列的任務結構體

對于任務隊列:

里面存放的都是函數指針,該函數指針指向的就是處理任務的函數;

同時還要維護一個任務函數的形參;

typedef struct Task
{
    void (*function)(void *args); //任務的函數指針
    void *args; //任務函數的形參
} Task;

2. 線程池的定義

線程池里面最重要的是:

一個任務隊列;
多個消費者線程IDs;
一個管理者線程ID;
管理線程池的鎖;
管理任務隊列是否為滿和空的條件變量;

還有一些其他的輔助成員變量;

struct ThreadPool
{
    Task *taskQ; //任務隊列
    /*對于一個任務隊列:我們需要知道以下信息*/
    int queueCapacity; //隊列的容量
    int queueSize;     //當前任務的個數
    int queueFront;    //隊頭取任務
    int queueRear;     //隊尾放任務

    /*有了任務隊列后,還要有管理任務隊列的線程和從任務隊列拿任務的線程*/
    pthread_t managerID; //管理者線程
    /*設置為指針的目的:工作線程有多個*/
    pthread_t *threadIDs; //工作線程(也就是消費者)

    /*對于工作線程我們要知道以下這幾個消息方便管理*/
    int minNum;  //最少的工作線程數
    int maxNum;  //最多的工作線程數
    int busyNum; //正在工作的線程數,也就是正在獲取任務處理的線程
    int liveNum; //存貨的工作線程數(也就是被喚醒的線程,卻沒有資格去獲取任務的線程)
    int exitNum; //銷毀的工作線程數(因為可能工作線程存在,但是卻不工作,我們需要殺掉一些不必要的線程)

    /*  由于任務隊列為臨界資源:
        工作線程(消費者)可能有多個會同時競爭該資源
        同時多生產者線程之間(也就是往任務隊列放任務的線程)也會競爭該資源
        所以我們要保證互斥訪問線程池的任務隊列
    */
    pthread_mutex_t mutexpool;    //鎖整個線程池
    pthread_mutex_t mutexbusyNum; //鎖增在工作線程的數量
    /*由于任務隊列滿,或者為空:
      生產者和消費者都需要阻塞
      所以需要條件變量,來保證
    */
    pthread_cond_t notFull;  //判斷線程池是否為滿
    pthread_cond_t notEmpty; //判斷線程池是否為空

    /*輔助成員主要判斷該線程池是否還在工作*/
    int shutdown; //判斷是否需要銷毀線程池,是0不銷毀,是1銷毀
};

線程池的頭文件聲明

#pragma once
#include < pthread.h >
#include < string.h >
#include < unistd.h >
#include < malloc.h >
#include< stdio.h >

typedef struct ThreadPool ThreadPool; //線程池結構體,這里聲明的原因是結構體定義在線程池源文件中

//創建線程池并初始化
ThreadPool* threadPoolCreate(int min,int max,int queueSize);

//銷毀線程池
int threadPoolDestroy(ThreadPool* pool);

//給線程池添加任務
void threadPoolAdd(ThreadPool* pool,void(*functions)(void*),void* args);

//獲取線程池工作線程的個數
int threadBusyNum (ThreadPool* pool);

//獲取線程池存活的線程的個數
int threadLiveNum (ThreadPool* pool);
//工作線程
void* worker (void* args);
//管理線程
void* manager (void* args);

//線程退出函數
void threadExit(ThreadPool* pool);

線程池的源文件

#include"thread_pool.h"

const int WORK_THREAD_NUMBER = 2; //管理者線程要添加的工作線程個數,和銷毀的線程個數
/*
線程池:首先要有個任務隊列,在C語言中,
任務隊列是需要自己定義的,C++中可以直接使用容器queue
*/
//任務隊列存放的任務就是一個函數指針
typedef struct Task
{
    void (*function)(void *args);
    void *args;
} Task;

//再搞出一個線程池

struct ThreadPool
{
    Task *taskQ; //任務隊列
    /*對于一個任務隊列:我們需要知道以下信息*/
    int queueCapacity; //隊列的容量
    int queueSize;     //當前任務的個數
    int queueFront;    //隊頭取任務
    int queueRear;     //隊尾放任務

    /*有了任務隊列后,還要有管理任務隊列的線程和從任務隊列拿任務的線程*/
    pthread_t managerID; //管理者線程
    /*設置為指針的目的:工作線程有多個*/
    pthread_t *threadIDs; //工作線程(也就是消費者)

    /*對于工作線程我們要知道以下這幾個消息方便管理*/
    int minNum;  //最少的工作線程數
    int maxNum;  //最多的工作線程數
    int busyNum; //正在工作的線程數,也就是正在獲取任務處理的線程
    int liveNum; //存貨的工作線程數(也就是被喚醒的線程,卻沒有資格去獲取任務的線程)
    int exitNum; //銷毀的工作線程數(因為可能工作線程存在,但是卻不工作,我們需要殺掉一些不必要的線程)

    /*  由于任務隊列為臨界資源:
        工作線程(消費者)可能有多個會同時競爭該資源
        同時多生產者線程之間(也就是往任務隊列放任務的線程)也會競爭該資源
        所以我們要保證互斥訪問線程池的任務隊列
    */
    pthread_mutex_t mutexpool;    //鎖整個線程池
    pthread_mutex_t mutexbusyNum; //鎖增在工作線程的數量
    /*由于任務隊列滿,或者為空:
      生產者和消費者都需要阻塞
      所以需要條件變量,來保證
    */
    pthread_cond_t notFull;  //判斷線程池是否為滿
    pthread_cond_t notEmpty; //判斷線程池是否為空

    /*輔助成員主要判斷該線程池是否還在工作*/
    int shutdown; //判斷是否需要銷毀線程池,是0不銷毀,是1銷毀
};

//************************************************************************************************

/*由于我們的線程池被創建出來時候,就必須保證存在的,
    所以我們返回值要設計為指針類型,不能是賦值拷貝的形式
    并且如何考慮線程池需要傳入什么參數初始化呢?
*/
ThreadPool *threadPoolCreate(int min, int max, int queueSize)
{
    //先搞出一個線程池
    ThreadPool *pool = (ThreadPool *)malloc(sizeof(ThreadPool));
    do // do while(0)的設計是為了,假設開辟線程池,消費者線程IDs,任務隊列空間失敗,可以直接跳出循環統一處理釋放空間
    {
        if (pool == NULL)
        {
            printf("malloc threadPool is failedn");
            break;
        }
        //搞出線程池后開始初始化里面的數據成員

        //首先先搞出消費者線程出來
        pool- >threadIDs = (pthread_t *)malloc(sizeof(pthread_t) * max);
        if (pool- >threadIDs == NULL)
        {
            printf("malloc threadIDs is failedn");
            /*如果沒有do while(0)的設計,這里直接返回,那么前面的pool內存池的空間沒有被釋放,這就會內存泄漏了*/
            // return NULL;

            //基于上面的注釋考慮,這里設計break;退出dowhile(0)然后處理
            break;
        }
        //初始化消費者線程ID
        /*這么做的目的是:在管理者線程中可以通過判斷線程ID是否為0,來說明該消費者線程是否被占用*/
        memset(pool- >threadIDs, 0, sizeof(pthread_t) * max);
        //初始化線程池的其他成員屬性
        pool- >minNum = min;
        pool- >maxNum = max;
        pool- >busyNum = 0;
        pool- >liveNum = min;
        pool- >exitNum = 0;
        //初始化鎖和條件變量
        if (pthread_mutex_init(&pool- >mutexpool, NULL) != 0 ||
            pthread_mutex_init(&pool- >mutexpool, NULL) != 0 ||
            pthread_cond_init(&pool- >notEmpty, NULL) != 0 ||
            pthread_cond_init(&pool- >notFull, NULL) != 0)
        {
            perror("mutex or condition failed:");
        }
        //初始化任務隊列
        pool- >taskQ = (Task *)malloc(sizeof(Task) * queueSize);
        if (pool- >taskQ == NULL)
        {
            printf("malloc taskQ is failedn");
            break;
        }
        pool- >queueCapacity = queueSize;
        pool- >queueSize = 0;
        pool- >queueFront = 0;
        pool- >queueRear = 0;
        //剛開始不關閉線程池
        pool- >shutdown = 0;

        //創建管理者線程和消費者線程
        pthread_create(&pool- >managerID, NULL, manager, (void *)pool);
        int i = 0;
        for (; i < min; ++i)
        {
            /*消費線程需要消費的是任務,
            也就是taskQ,而taskQ又是pool的一個成員屬性
            所以傳參時候,我們傳入pool就可以獲得taskQ了
            */
            pthread_create(&pool- >threadIDs[i], NULL, worker, (void *)pool);
        }

        //創建成功初始化后,那么就可以把線程池返回去了
        return pool;
    } while (0);
    //如果break出來,那么就是異常的開辟空間失敗,要釋放資源
    if (pool)
        free(pool);
    if (pool && pool- >threadIDs)
        free(pool- >threadIDs);
    if (pool && pool- >taskQ)
        free(pool- >taskQ);

    return NULL;
}

//判斷任務隊列是否為空
static int taskQIsEmpty(ThreadPool *pool)
{
    return pool- >queueSize == 0;
}
//判斷線程池是否還工作
static int isShutDown(ThreadPool *pool)
{
    return pool- >shutdown == 1 ? 1 : 0;
}

//消費者線程
void *worker(void *args)
{
    ThreadPool *pool = (ThreadPool *)args;
    /*設計為死循環是:消費者要不斷從任務隊列拿任務來處理*/
    while (1)
    {
        pthread_mutex_lock(&pool- >mutexpool);
        //消費數據之前,要判斷任務隊列是否為空,空就需要掛起該線程
        while (taskQIsEmpty(pool) && !isShutDown(pool))
        {
            pthread_cond_wait(&pool- >notEmpty, &pool- >mutexpool);

            //線程被喚醒后,判斷是否需要銷毀該線程,因為有線程是多余的
            if (pool- >exitNum > 0)
            {
                pool- >exitNum--;
                if (pool- >liveNum > pool- >minNum)
                {
                    pool- >liveNum--;
                    pthread_mutex_unlock(&pool- >mutexpool); //退出線程前解鎖,防止死鎖問題
                    threadExit(pool);
                }
            }
        }
        //還需要判斷線程池是否關閉了,關閉了就退出消費者線程即可
        if (isShutDown(pool))
        {
            pthread_mutex_unlock(&pool- >mutexpool);
            threadExit(pool);
        }
        //開始消費者拿任務
        Task task;                                              //保存任務的變量
        task.function = pool- >taskQ[pool- >queueFront].function; //獲取到任務隊列的任務,就是一個函數指針
        task.args = pool- >taskQ[pool- >queueFront].args;           //獲取任務隊列任務的函數指針參數

        //控制任務隊列的指針移動
        pool- >queueFront++;
        pool- >queueFront %= pool- >queueCapacity;
        pool- >queueSize--;

        pthread_mutex_unlock(&pool- >mutexpool);
         //喚醒生產者
        pthread_cond_signal(&pool- >notFull);

        //拿到任務后就是處理任務

        // 1.處理任務前,先處理busyNum
        pthread_mutex_lock(&pool- >mutexbusyNum);
        pool- >busyNum++;
        pthread_mutex_unlock(&pool- >mutexbusyNum);

        // 2. 這里處理任務就是調用任務函數
        task.function(task.args);
        //任務處理完就釋放參數的空間
        free(task.args);
        task.args = NULL;

        printf("thread %ld ending working ... n", pthread_self());
        // 3.處理完任務對其busyNum操作
        pthread_mutex_lock(&pool- >mutexbusyNum);
        pool- >busyNum--;
        pthread_mutex_unlock(&pool- >mutexbusyNum);
    }
}
//管理者線程
/*
主要是管理創建線程和銷毀線程

*/
void *manager(void *args)
{
    ThreadPool *pool = (ThreadPool *)args;
    //只要線程池沒關閉,那么管理者線程就一直工作
    while (!isShutDown(pool))
    {
        //自己定制的檢查策略:我設置每個三秒檢測
        sleep(3);

        //取出線程池任務的數量和消費者的工作線程數量
        pthread_mutex_lock(&pool- >mutexpool);
        int queueSize = pool- >queueSize;
        int liveNum = pool- >liveNum;
        pthread_mutex_unlock(&pool- >mutexpool);

        //獲取忙的消費者線程數量
        pthread_mutex_lock(&pool- >mutexbusyNum);
        int busyNum = pool- >busyNum;
        pthread_mutex_unlock(&pool- >mutexbusyNum);

        //開始管理線程
        // 1.添加消費者線程
        /*制定添加規則(也是自己設定的)
            任務的個數 > 存活的線程個數 && 存活的線程個數 < 最大的線程個數
        */
        if (queueSize > liveNum && liveNum < pool- >maxNum)
        {
            pthread_mutex_lock(&pool- >mutexpool); //這個鎖主要是操作了liveNum這個資源

            int counter = 0; // counter表示要添加的消費者線程數量
            //遍歷 消費者線程IDs數組,看看哪個位置可以放入新添加的線程
            int i = 0;
            for (; i < pool- >maxNum &&
                   counter < WORK_THREAD_NUMBER &&
                   pool- >liveNum < pool- >maxNum;
                 i++)
            {
                //為0表示消費者線程數組的位置可以放入線程ID
                if (pool- >threadIDs[i] == 0)
                {
                    pthread_create(&pool- >threadIDs[i], NULL, worker, pool);
                    counter++;
                    liveNum++;
                }
            }
            pthread_mutex_unlock(&pool- >mutexpool);
        }

        //由于線程過多,可能要進行銷毀
        // 2. 銷毀消費者線程
        /*
             銷毀線程的策略:
              存活的線程數量 >忙的線程數量*2 && 存活線程數量 >最小線程數量
        */
        if (liveNum > busyNum * 2 && liveNum > pool- >minNum)
        {
            pthread_mutex_lock(&pool- >mutexpool);
            pool- >exitNum = WORK_THREAD_NUMBER;
            pthread_mutex_unlock(&pool- >mutexpool);

            //讓工作者線程去自殺
            /*如何讓他自殺呢?
              由于線程池有多余的消費者線程不工作
              我們可以通過喚醒消費者線程,讓他去自己消亡
            */
            int i = 0;
            for (; i < WORK_THREAD_NUMBER; i++)
            {
                pthread_cond_signal(&pool- >notEmpty);
            }
        }
    }
}

//線程退出函數
void threadExit(ThreadPool *pool)
{
    pthread_t tid = pthread_self();
    int i = 0;
    //遍歷消費者線程的線程個數,找到退出線程的ID
    for (; i < pool- >maxNum; i++)
    {
        if (pool- >threadIDs[i] == tid)
        {
            pool- >threadIDs[i] = 0;
            printf("threadExit()消費者線程 :%ld exit...n", tid);
            break;
        }
    }
    pthread_exit(NULL);
}
static int taskQisFull(ThreadPool* pool)
{
    return pool- >queueCapacity == pool- >queueSize;
}
//給線程池添加任務
void threadPoolAdd(ThreadPool* pool,void(*function)(void*),void* args)
{
    pthread_mutex_lock(&pool- >mutexpool); 
    //生產者線程:任務隊列滿要阻塞自己
    while(taskQisFull(pool) && !isShutDown(pool))
    {
        pthread_cond_wait(&pool- >notFull,&pool- >mutexpool);
    }
    if(isShutDown(pool))
    {
        pthread_mutex_unlock(&pool- >mutexpool);
        return ;
    }

    //添加任務
    pool- >taskQ[pool- >queueRear].function = function;
    pool- >taskQ[pool- >queueRear].args = args;

    pool- >queueRear++;
    pool- >queueRear %= pool- >queueCapacity;
    pool- >queueSize++;

    pthread_mutex_unlock(&pool- >mutexpool); 
    //喚醒work線程:
    pthread_cond_signal(&pool- >notEmpty);
}

//獲取線程池工作線程的個數
int threadBusyNum (ThreadPool* pool)
{
    pthread_mutex_lock(&pool- >mutexbusyNum);
    int busyNum = pool- >busyNum;
    pthread_mutex_unlock(&pool- >mutexbusyNum);
    return busyNum;

}

//獲取線程池存活的線程的個數
int threadLiveNum (ThreadPool* pool)
{
    pthread_mutex_lock(&pool- >mutexpool);
    int liveNum = pool- >liveNum;
    pthread_mutex_unlock(&pool- >mutexpool);
    return liveNum;
}

//銷毀線程池
int threadPoolDestroy(ThreadPool* pool)
{
    if(pool == NULL)
    {
        return -1;
    }
    //關閉線程池
    pool- >shutdown = 1;


    //喚醒阻塞的消費者
    //存活的線程有多少就喚醒多少
    int i = 0;
    for(;i < pool- >liveNum;i++)
    {
        pthread_cond_signal(&pool- >notEmpty);
    }
    pthread_join(pool- >managerID,NULL);

    //釋放資源
    if(pool- >taskQ )
        free(pool- >taskQ);
    if(pool- >threadIDs)
        free(pool- >threadIDs);

    pthread_mutex_destroy(&pool- >mutexbusyNum);
    pthread_mutex_destroy(&pool- >mutexpool);
    pthread_cond_destroy(&pool- >notFull);
    pthread_cond_destroy(&pool- >notEmpty);

    free(pool);
    pool = NULL;

    return 0;

}

線程池測試代碼

#include"thread_pool.h"

//任務處理函數
void taskFunction(void* args)
{
    int num = *(int*)args;
    printf("thread: %ld is working,number:%dn",pthread_self(),num);
    sleep(1);
}
int main()
{
    //創建線程池
    ThreadPool* pool = threadPoolCreate(3,10,20);

    //往線程池里面放任務
    int i = 0;
    for(; i< 20; i++)
    {
        int *num = (int*)malloc(sizeof(int));
        *num = i+1;
        threadPoolAdd(pool,taskFunction,(void*)num);
    }

    sleep(10);

    threadPoolDestroy(pool);
    return 0;
}

測試線程池結果

由于我的測試代碼:只搞了3個工作線程(消費者線程),任務隊列大小為20,并且搞了20個任務隊列進去,所以線程池就會有三個工作線程在搶奪任務工作!

圖片

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 程序
    +關注

    關注

    117

    文章

    3815

    瀏覽量

    82018
  • 容器
    +關注

    關注

    0

    文章

    503

    瀏覽量

    22303
  • 線程池
    +關注

    關注

    0

    文章

    57

    瀏覽量

    7032
  • 數組
    +關注

    關注

    1

    文章

    419

    瀏覽量

    26256
收藏 人收藏

    評論

    相關推薦

    跨平臺的線程組件--TP組件

    /銷毀代價是很高的。那么我們要怎么去設計多線程編程呢???答案:對于長駐的線程,我們可以創建獨立的線程去執行。但是非長駐的線程,我們可以通過線程
    的頭像 發表于 04-06 15:39 ?1025次閱讀

    Java中的線程包括哪些

    線程是用來統一管理線程的,在 Java 中創建和銷毀線程都是一件消耗資源的事情,線程可以重復
    的頭像 發表于 10-11 15:33 ?935次閱讀
    Java中的<b class='flag-5'>線程</b><b class='flag-5'>池</b>包括哪些

    線程是如何實現的

    線程概念是什么?線程是如何實現的?
    發表于 02-28 06:20

    基于線程技術集群接入點的應用研究

    本文在深入研究高級線程技術的基礎上,分析、研究了固定線程數目的線程線程數目動態變化的
    發表于 01-22 14:21 ?5次下載

    python創建線程的兩種方法

    在使用多線程處理任務時也不是線程越多越好,由于在切換線程的時候,需要切換上下文環境,依然會造成cpu的大量開銷。為解決這個問題,線程
    的頭像 發表于 03-16 16:15 ?6124次閱讀

    基于Nacos的簡單動態化線程實現

    本文以Nacos作為服務配置中心,以修改線程核心線程數、最大線程數為例,實現一個簡單的動態化線程
    發表于 01-06 14:14 ?994次閱讀

    線程線程

    線程通常用于服務器應用程序。 每個傳入請求都將分配給線程池中的一個線程,因此可以異步處理請求,而不會占用主線程,也不會延遲后續請求的處理
    的頭像 發表于 02-28 09:53 ?941次閱讀
    多<b class='flag-5'>線程</b>之<b class='flag-5'>線程</b><b class='flag-5'>池</b>

    Java線程核心原理

    看過Java線程源碼的小伙伴都知道,在Java線程池中最核心的類就是ThreadPoolExecutor,
    的頭像 發表于 04-21 10:24 ?993次閱讀

    細數線程的10個坑

    JDK開發者提供了線程的實現類,我們基于Executors組件,就可以快速創建一個線程
    的頭像 發表于 06-16 10:11 ?834次閱讀
    細數<b class='flag-5'>線程</b><b class='flag-5'>池</b>的10個坑

    線程線程怎么釋放

    線程分組看,pool名開頭線程占616條,而且waiting狀態也是616條,這個點就非常可疑了,我斷定就是這個pool開頭線程導致的問題。我們先排查為何這個
    發表于 07-31 10:49 ?2453次閱讀
    <b class='flag-5'>線程</b><b class='flag-5'>池</b>的<b class='flag-5'>線程</b>怎么釋放

    Spring 的線程應用

    我們在日常開發中,經常跟多線程打交道,Spring 為我們提供了一個線程方便我們開發,它就是 ThreadPoolTaskExecutor ,接下來我們就來聊聊 Spring 的線程
    的頭像 發表于 10-13 10:47 ?710次閱讀
    Spring 的<b class='flag-5'>線程</b><b class='flag-5'>池</b>應用

    線程基本概念與原理

    一、線程基本概念與原理 1.1 線程概念及優勢 C++
    的頭像 發表于 11-10 10:24 ?770次閱讀

    線程七大核心參數執行順序

    線程是一種用于管理和調度線程執行的技術,通過將任務分配到線程池中的線程進行處理,可以有效地控制并發線程
    的頭像 發表于 12-04 16:45 ?1315次閱讀

    線程的創建方式有幾種

    線程是一種用于管理和調度線程的技術,能夠有效地提高系統的性能和資源利用率。它通過預先創建一組線程并維護一個工作隊列,將任務提交給線程
    的頭像 發表于 12-04 16:52 ?1084次閱讀

    什么是動態線程?動態線程的簡單實現思路

    因此,動態可監控線程一種針對以上痛點開發的線程管理工具。主要可實現功能有:提供對 Spring 應用內線程
    的頭像 發表于 02-28 10:42 ?910次閱讀
    主站蜘蛛池模板: 天堂网在线资源www种子 | 欧美一级特黄aaaaaa在线看首页 | 三级视频网站在线观看 | 中国理论片 | 色婷婷综合久久久中文字幕 | 欧美一级黄色片 | 四虎影视精品 | 六月丁香六月婷婷 | 久久久久久久国产免费看 | 免费特黄 | 美女免费视频一区二区三区 | 天天操天天干天天爽 | 男人天堂欧美 | 免费黄色a视频 | 99热精品久久只有精品30 | 欧美一二三区在线 | 一级做a爰片久久毛片毛片 一级做a爰片久久毛片美女图片 | 日本高清视频色 | 一级毛片女人喷潮 | 三级黄网站 | 精品亚洲午夜久久久久 | 在线看欧美成人中文字幕视频 | 午夜骚| 午夜视频在线观看免费视频 | 欧美一级做一a做片性视频 欧美一级做一级做片性十三 | 5x性区m免费毛片视频看看 | 亚洲成人综合在线 | 免费一级黄色录像 | 一区二区福利 | 免费的黄色的视频 | 永久国产 | 在线午夜视频 | 亚洲日本在线观看视频 | 中文字幕一区二区三区乱码aⅴ | 亚洲婷婷六月 | 人人爽影院 | 玖玖在线免费视频 | 久久成人性色生活片 | 激情综合五月网 | 国产伦精品一区二区三区网站 | 亚洲伊人tv综合网色 |