日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁(yè) > 單片機(jī) > 程序喵大人

開(kāi)源項(xiàng)目Workflow中有個(gè)重要的基礎(chǔ)模塊:
代碼僅300行的C語(yǔ)言線程池。

本文會(huì)伴隨源碼分析,而邏輯完備、對(duì)稱(chēng)無(wú)差別的特點(diǎn)于第3部分開(kāi)始
歡迎跳閱, 或直接到Github主頁(yè)上圍觀代碼?

https://github.com/sogou/workflow/blob/master/src/kernel/thrdpool.c

? 0 - Workflow的thrdpool

作為目前Github上炙手可熱的異步調(diào)度引擎
Workflow有一個(gè)大招是:
計(jì)算通信融為一體。
而計(jì)算的核心:Executor調(diào)度器
就是基于這個(gè)線程池實(shí)現(xiàn)的。
可以說(shuō),一個(gè)通用而高效的線程池,
是我們寫(xiě)C/C++代碼時(shí)離不開(kāi)的基礎(chǔ)模塊。

thrdpool代碼位置在src/kernel/,
不僅可以直接拿來(lái)使用,
同時(shí)也適合閱讀學(xué)習(xí)。

而更重要的,秉承Workflow項(xiàng)目本身
一貫的嚴(yán)謹(jǐn)極簡(jiǎn)的作風(fēng),
這個(gè)thrdpool代碼極致簡(jiǎn)潔,
實(shí)現(xiàn)邏輯上亦非常完備,
結(jié)構(gòu)精巧,處處嚴(yán)謹(jǐn),
復(fù)雜的并發(fā)處理依然可以對(duì)稱(chēng)無(wú)差別,
不得不讓我驚嘆:妙?。。?!?

你可能會(huì)很好奇,
線程池還能寫(xiě)出什么別致的新思路嗎?
先列出一些,你們細(xì)品:

  • 特點(diǎn)1:創(chuàng)建完線程池后,無(wú)需記錄任何線程id或?qū)ο?,線程池可以通過(guò)一個(gè)等一個(gè)的方式優(yōu)雅地去結(jié)束所有線程;? 也就是說(shuō),每一個(gè)線程都是對(duì)等的
  • 特點(diǎn)2:線程任務(wù)可以由另一個(gè)線程任務(wù)調(diào)起;甚至線程池正在被銷(xiāo)毀時(shí)也可以提交下一個(gè)任務(wù);(這很重要,因?yàn)榫€程本身很可能是不知道線程池的狀態(tài)的;? 即,每一個(gè)任務(wù)也是對(duì)等的
  • 特點(diǎn)3:同理,線程任務(wù)也可以銷(xiāo)毀這個(gè)線程池;(非常完整~? 每一種行為也是對(duì)等的,包括destroy

我真的迫不及待為大家深層解讀一下,
這個(gè)我愿稱(chēng)之為“邏輯完備”的線程池。

? 1 - 前置知識(shí)

第一部分我先梳理一些基本內(nèi)容梳理,
有基礎(chǔ)的小伙伴可以直接跳過(guò)。
如果有不準(zhǔn)確的地方,歡迎大家指正交流~

Question: 為什么需要線程池?
其實(shí)思路不僅對(duì)線程池,
對(duì)任何有限資源的調(diào)度管理都是類(lèi)似的。

我們知道,
通過(guò)pthread或者std::thread創(chuàng)建線程,
就可以實(shí)現(xiàn)多線程并發(fā)執(zhí)行我們的代碼。

但是CPU的核數(shù)是固定的,
所以真正并行執(zhí)行的最大值也是固定的,
過(guò)多的線程除了頻繁創(chuàng)建產(chǎn)生overhead以外,
還會(huì)導(dǎo)致對(duì)系統(tǒng)資源進(jìn)行爭(zhēng)搶
,
這些都是不必要的浪費(fèi)。

因此我們可以管理有限個(gè)線程,
循環(huán)且合理地利用它們。??

那么線程池一般包含哪些內(nèi)容呢?

  • 首先是管理若干個(gè)工具人線程;
  • 其次是管理交給線程去執(zhí)行的任務(wù),這個(gè)一般會(huì)有一個(gè)隊(duì)列;
  • 再然后線程之間需要一些同步機(jī)制,比如mutex、condition等;
  • 最后就是各線程池實(shí)現(xiàn)上自身需要的其他內(nèi)容了;

接下來(lái)我們看看Workflowthrdpool是怎么做的。

? 2 - 代碼概覽

以下共7步常用思路,
足以讓我們把代碼飛快過(guò)一遍。

第1步:先看頭文件,有什么接口。

我們打開(kāi)thrdpool.h,只需關(guān)注這三個(gè):

// 創(chuàng)建線程池 thrdpool_t *thrdpool_create(size_t nthreads, size_t stacksize); // 把任務(wù)交給線程池的入口 int thrdpool_schedule(const struct thrdpool_task *task, thrdpool_t *pool); // 銷(xiāo)毀線程池 void thrdpool_destroy(void (*pending)(const struct thrdpool_task *), thrdpool_t *pool);

第2步:接口上有什么數(shù)據(jù)結(jié)構(gòu)。

即,我們?nèi)绾蚊枋鲆粋€(gè)交給線程池的任務(wù)。

struct thrdpool_task { void (*routine)(void *); // 函數(shù)指針 void *context; // 上下文 };

第3步:再看實(shí)現(xiàn).c,內(nèi)部數(shù)據(jù)結(jié)構(gòu)。

struct __thrdpool { struct list_head task_queue;// 任務(wù)隊(duì)列 size_t nthreads; // 線程個(gè)數(shù) size_t stacksize; // 構(gòu)造線程時(shí)的參數(shù) pthread_t tid; // 運(yùn)行期間記錄的是個(gè)zero值 pthread_mutex_t mutex; pthread_cond_t cond; pthread_key_t key; pthread_cond_t *terminate;
};

沒(méi)有一個(gè)多余,每一個(gè)成員都很到位:

  1. tid:線程id,整個(gè)線程池只有一個(gè),它不會(huì)奇怪地去記錄任何一個(gè)線程的id,這樣就不完美了?,它平時(shí)運(yùn)行的時(shí)候是空值,退出的時(shí)候,它是用來(lái)實(shí)現(xiàn)鏈?zhǔn)降却年P(guān)鍵。
  2. mutexcond是常見(jiàn)的線程間同步的工具,其中這個(gè)cond是用來(lái)給生產(chǎn)者和消費(fèi)者去操作任務(wù)隊(duì)列用的。
  3. key:是線程池的key,然后會(huì)賦予給每個(gè)由線程池創(chuàng)建的線程作為他們的thread local,用于區(qū)分這個(gè)線程是否是線程池創(chuàng)建的。
  4. 一個(gè)pthread_cond_t *terminate,這有兩個(gè)用途:不僅是退出時(shí)的標(biāo)記位 ,而且還是調(diào)用退出的那個(gè)人要等待的condition。

以上各個(gè)成員的用途,
好像說(shuō)了,又好像沒(méi)說(shuō),?
是因?yàn)?strong>幾乎每一個(gè)成員都值得深挖一下,
所以我們記住它們,
后面看代碼的時(shí)候就會(huì)豁然開(kāi)朗!?

第4步:接口都調(diào)用了什么核心函數(shù)。

thrdpool_t *thrdpool_create(size_t nthreads, size_t stacksize) { thrdpool_t *pool;
    ret = pthread_key_create(&pool->key, NULL); if (ret == 0)
    { // 去掉了其他代碼,但是注意到剛才的tid和terminate的賦值 memset(&pool->tid, 0, sizeof (pthread_t));
        pool->terminate = NULL; if (__thrdpool_create_threads(nthreads, pool) >= 0) return pool;
        ...

這里可以看到
__thrdpool_create_threads()里邊
最關(guān)鍵的,就是循環(huán)創(chuàng)建nthreads個(gè)線程。

while (pool->nthreads < nthreads) { ret = pthread_create(&tid, &attr, __thrdpool_routine, pool); ...

第5步:略讀核心函數(shù)的功能。

所以我們?cè)谏弦徊街懒耍?br /> 每個(gè)線程執(zhí)行的是__thrdpool_routine()
不難想象,它會(huì)不停從隊(duì)列拿任務(wù)出來(lái)執(zhí)行

static void *__thrdpool_routine(void *arg)                                      
{                                                                               
    ... while (1)                                                                   
    { // 1. 從隊(duì)列里拿一個(gè)任務(wù)出來(lái),沒(méi)有就等待 pthread_mutex_lock(&pool->mutex); while (!pool->terminate && list_empty(&pool->task_queue))
            pthread_cond_wait(&pool->cond, &pool->mutex); // 2. 線程池結(jié)束的標(biāo)志位,記住它,先跳過(guò) if (pool->terminate) break; // 3. 如果能走到這里,恭喜你,拿到了任務(wù)~  entry = list_entry(*pos, struct __thrdpool_task_entry, list);
        list_del(*pos); // 4. 先解鎖 pthread_mutex_unlock(&pool->mutex); 

        task_routine = entry->task.routine;
        task_context = entry->task.context; free(entry); // 5. 再執(zhí)行 task_routine(task_context); // 6. 這里也先記住它,意思是線程池里的線程可以銷(xiāo)毀線程池 if (pool->nthreads == 0) 
        { /* Thread pool was destroyed by the task. */ free(pool); return NULL;
        }                                                                       
    }
    ... // 后面還有魔法,留下一章解讀~~~ 

第6步:把函數(shù)之間的關(guān)系聯(lián)系起來(lái)。

剛才看到的__thrdpool_routine()
就是線程的核心函數(shù)了,
它可以和誰(shuí)關(guān)聯(lián)起來(lái)呢?
可以和接口thrdpool_schedule()關(guān)聯(lián)上

我們說(shuō)過(guò),線程池上有個(gè)隊(duì)列管理任務(wù):

  • 每個(gè)執(zhí)行routine的線程,都是消費(fèi)者
  • 每個(gè)發(fā)起schedule的線程,都是生產(chǎn)者

我們已經(jīng)看過(guò)消費(fèi)者了,來(lái)看看生產(chǎn)者的代碼:

inline void __thrdpool_schedule(const struct thrdpool_task *task, void *buf, thrdpool_t *pool)
{ struct __thrdpool_task_entry *entry = (struct __thrdpool_task_entry *)buf; entry->task = *task;
    pthread_mutex_lock(&pool->mutex); // 添加到隊(duì)列里 list_add_tail(&entry->list, &pool->task_queue); // 叫醒在等待的線程 pthread_cond_signal(&pool->cond);
    pthread_mutex_unlock(&pool->mutex);
}

說(shuō)到這里,特點(diǎn)2就非常清晰了:

開(kāi)篇說(shuō)的特點(diǎn)2是說(shuō),
線程任務(wù)可以由另一個(gè)線程任務(wù)調(diào)起”。

只要對(duì)隊(duì)列的管理做得好,
顯然消費(fèi)者所執(zhí)行的函數(shù)里也可以生產(chǎn)

第7步:看其他情況的處理

對(duì)于線程池來(lái)說(shuō)就是比如銷(xiāo)毀的情況。

接口thrdpool_destroy()實(shí)現(xiàn)非常簡(jiǎn)單:

void thrdpool_destroy(void (*pending)(const struct thrdpool_task *), thrdpool_t *pool) {
    ... // 1. 內(nèi)部會(huì)設(shè)置pool->terminate,并叫醒所有等在隊(duì)列拿任務(wù)的線程 __thrdpool_terminate(in_pool, pool); // 2. 把隊(duì)列里還沒(méi)有執(zhí)行的任務(wù)都拿出來(lái),通過(guò)pending返回給用戶(hù) list_for_each_safe(pos, tmp, &pool->task_queue)                             
    {
        entry = list_entry(pos, struct __thrdpool_task_entry, list);            
        list_del(pos); if (pending)                                                            
            pending(&entry->task);                                              
        ... // 后面就是銷(xiāo)毀各種內(nèi)存,同樣有魔法~ 

在退出的時(shí)候,
我們那些已經(jīng)提交但是還沒(méi)有被執(zhí)行的任務(wù)
是絕對(duì)不能就這么扔掉了的,
于是我們可以傳入一個(gè)pending()函數(shù),
上層可以做自己的回收、回調(diào)、
或任何保證上層邏輯完備的事情
。

設(shè)計(jì)的完整性,無(wú)處不在。

接下來(lái)我們就可以跟著我們的核心問(wèn)題,
針對(duì)性地看看每個(gè)特點(diǎn)都是怎么實(shí)現(xiàn)的。

? 3 - 特點(diǎn)1: 一個(gè)等待一個(gè)的優(yōu)雅退出

這里提出一個(gè)問(wèn)題:
線程池要退出,如何結(jié)束所有線程?

一般線程池的實(shí)現(xiàn)都是
需要記錄下所有的線程id,
或者thread對(duì)象,
以便于我們?nèi)ビ?strong>join方法等待它們結(jié)束。

不嚴(yán)格地用join收拾干凈會(huì)有什么問(wèn)題?
最直觀的,模塊退出時(shí)很可能會(huì)報(bào)內(nèi)存泄漏

但是我們剛才看,
pool里并沒(méi)有記錄所有的tid呀
正如開(kāi)篇說(shuō)的,
pool上只有一個(gè)tid,而且還是個(gè)空的值。

而特點(diǎn)1正是Workflow thrdpool的答案:

無(wú)需記錄所有線程,我可以讓線程挨個(gè)自動(dòng)退出、且一個(gè)等待下一個(gè),最終達(dá)到調(diào)用完thrdpool_destroy()后內(nèi)存回收干凈的目的。

這里先給一個(gè)簡(jiǎn)單的圖,
假設(shè)發(fā)起destroy的人是main線程,
我們?nèi)绾巫龅揭粋€(gè)等一個(gè)退出:

外部線程,比如main,發(fā)起destroy

步驟如下:

  1. 線程的退出,由thrdpool_destroy()設(shè)置pool->terminate開(kāi)始。
  2. 我們每個(gè)線程,在while(1) 里會(huì)第一時(shí)間發(fā)現(xiàn)terminate,線程池要退出了,然后會(huì)break出這個(gè)while循環(huán)。
  3. 注意這個(gè)時(shí)候,還持有著mutex鎖,我們拿出pool上唯一的那個(gè)tid,放到我的臨時(shí)變量,我會(huì)根據(jù)拿出來(lái)的值做不同的處理。且我會(huì)把我自己的tid放上去,然后再解mutex鎖。
  4. 那么很顯然,第一個(gè)從pool上拿tid的人,會(huì)發(fā)現(xiàn)這是個(gè)0值,就可以直接結(jié)束了,不用負(fù)責(zé)等待任何其他人,但我在完全結(jié)束之前需要有人負(fù)責(zé)等待我的結(jié)束,所以我會(huì)把我的id放上去。
  5. 而如果發(fā)現(xiàn)自己從pool里拿到的tid不是0值,說(shuō)明我要負(fù)責(zé)join上一個(gè)人,并且把我的tid放上去,讓下一個(gè)人負(fù)責(zé)我。
  6. 最后的那個(gè)人,是那個(gè)發(fā)現(xiàn)pool->nthreads為0的人,那么我就可以通過(guò)這個(gè)terminate(它本身是個(gè)condition)去通知發(fā)起destroy的人。
  7. 最后發(fā)起者就可以退了。?

是不是非常有意思?。?!
非常優(yōu)雅的做法?。?!?

所以我們會(huì)發(fā)現(xiàn),
其實(shí)大家不太需要知道太多信息,
只需要知道我要負(fù)責(zé)的上一個(gè)人
。

當(dāng)然每一步都是非常嚴(yán)謹(jǐn)?shù)模?br /> 結(jié)合剛才跳過(guò)的第一段魔法?感受一下:

static void *__thrdpool_routine(void *arg)                                         
{ while (1)
    { // 1.注意這里還持有鎖 pthread_mutex_lock(&pool->mutex); 
        ... // 等著隊(duì)列拿任務(wù)出來(lái) // 2. 這既是標(biāo)識(shí)位,也是發(fā)起銷(xiāo)毀的那個(gè)人所等待的condition if (pool->terminate) break;
        ... // 執(zhí)行拿到的任務(wù) } /* One thread joins another. Don't need to keep all thread IDs. */ // 3. 把線程池上記錄的那個(gè)tid拿下來(lái),我來(lái)負(fù)責(zé)上一人 tid = pool->tid; // 4. 把我自己記錄到線程池上,下一個(gè)人來(lái)負(fù)責(zé)我 pool->tid = pthread_self(); // 5. 每個(gè)人都減1,最后一個(gè)人負(fù)責(zé)叫醒發(fā)起detroy的人 if (--pool->nthreads == 0) 
        pthread_cond_signal(pool->terminate); // 6. 這里可以解鎖進(jìn)行等待了 pthread_mutex_unlock(&pool->mutex); // 7. 只有第一個(gè)人拿到0值 if (memcmp(&tid, &__zero_tid, sizeof (pthread_t)) != 0) // 8. 只要不0值,我就要負(fù)責(zé)等上一個(gè)結(jié)束才能退 pthread_join(tid, NULL); return NULL; // 9. 退出,干干凈凈~ }

? 4 - 特點(diǎn)2:線程任務(wù)可以由另一個(gè)線程任務(wù)調(diào)起

在第二部分我們看過(guò)源碼,
只要隊(duì)列管理得好,
線程任務(wù)里提交下一個(gè)任務(wù)是完全OK的。

這很合理。?

那么問(wèn)題來(lái)了,
特點(diǎn)1又說(shuō),我們每個(gè)線程,
是不需要知道太多線程池的狀態(tài)和信息的。
而線程池的銷(xiāo)毀是個(gè)過(guò)程,
如果在這個(gè)過(guò)程間提交任務(wù)會(huì)怎么樣呢?

因此特點(diǎn)2的一個(gè)重要解讀是:
線程池被銷(xiāo)毀時(shí)也可以提交下一個(gè)任務(wù)。
而且剛才提過(guò),
還沒(méi)有被執(zhí)行的任務(wù),
可以通過(guò)我們傳入的pending()函數(shù)拿回來(lái)。

簡(jiǎn)單看看銷(xiāo)毀時(shí)的嚴(yán)謹(jǐn)做法:

static void __thrdpool_terminate(int in_pool, thrdpool_t *pool)                 
{ pthread_cond_t term = PTHREAD_COND_INITIALIZER;
    pthread_mutex_lock(&pool->mutex); // 1. 加鎖設(shè)置標(biāo)志位,之后的添加任務(wù)不會(huì)被執(zhí)行,但可以pending拿到 pool->terminate = &term; // 2. 廣播所有等待的消費(fèi)者 pthread_cond_broadcast(&pool->cond); if (in_pool) // 3. 這里的魔法等下講>_<~ { /* Thread pool destroyed in a pool thread is legal. */ pthread_detach(pthread_self());                                         
        pool->nthreads--;                                                       
    } // 4. 如果還有線程沒(méi)有退完,我會(huì)等,注意這里是while while (pool->nthreads > 0) 
        pthread_cond_wait(&term, &pool->mutex);                                 

    pthread_mutex_unlock(&pool->mutex); // 5.同樣地等待打算退出的上一個(gè)人  if (memcmp(&pool->tid, &__zero_tid, sizeof (pthread_t)) != 0)               
        pthread_join(pool->tid, NULL); 
}

? 5 - 特點(diǎn)3:同樣可以在線程任務(wù)里銷(xiāo)毀這個(gè)線程池

既然線程任務(wù)可以做任何事情,
理論上,線程任務(wù)也可以銷(xiāo)毀線程池?

作為一個(gè)邏輯完備的線程池,
大膽一點(diǎn),
我們把問(wèn)號(hào)去掉。

而且,銷(xiāo)毀并不會(huì)結(jié)束當(dāng)前任務(wù),
它會(huì)等這個(gè)任務(wù)執(zhí)行完
。

想象一下,剛才的__thrdpool_routine()
while(1)里拿出來(lái)的那個(gè)任務(wù),
做的事情竟然是發(fā)起thrdpool_destroy()...

把上面的圖大膽改一下:?

我們讓一個(gè)routine來(lái)destroy線程池

如果發(fā)起銷(xiāo)毀的人,
是我們自己內(nèi)部的線程,
那么我們就不是等n個(gè),而是等n-1,
少了一個(gè)外部線程等待我們。
如何實(shí)現(xiàn)才能讓這些邏輯都完美融合呢?
我們把剛才跳過(guò)的三段魔法串起來(lái)看看。

? 第一段魔法,銷(xiāo)毀的發(fā)起者。

如果發(fā)起銷(xiāo)毀的人是線程池內(nèi)部的線程,
那么它具有較強(qiáng)的自我管理意識(shí)

(因?yàn)榍懊嬲f(shuō)了,會(huì)等它這個(gè)任務(wù)執(zhí)行完)
而我們可以放心大膽地pthread_detach,
無(wú)需任何人join它等待它結(jié)束。

static void __thrdpool_terminate(int in_pool, thrdpool_t *pool)                 
{ 
    ... // 每個(gè)由線程池創(chuàng)建的線程都設(shè)置了一個(gè)key,由此判斷是否是in_pool if (in_pool) 
    { /* Thread pool destroyed in a pool thread is legal. */ pthread_detach(pthread_self());
        pool->nthreads--;
    }

? 第二段魔法:線程池誰(shuí)來(lái)free?

一定是發(fā)起銷(xiāo)毀的那個(gè)人。
所以這里用in_pool來(lái)判斷是否是外部的人:

void thrdpool_destroy(void (*pending)(const struct thrdpool_task *), thrdpool_t *pool) { // 已經(jīng)調(diào)用完第一段,且挨個(gè)pending(未執(zhí)行的task)了 // 銷(xiāo)毀其他內(nèi)部分配的內(nèi)存 ... // 如果不是內(nèi)部線程發(fā)起的銷(xiāo)毀,要負(fù)責(zé)回收線程池內(nèi)存 if (!in_pool) free(pool);
}

那現(xiàn)在不是main線程發(fā)起的銷(xiāo)毀呢?
發(fā)起的銷(xiāo)毀的那個(gè)內(nèi)部線程,
怎么能保證我可以在最后關(guān)頭
把所有資源回收干凈、調(diào)free(pool)、
最后功成身退呢
?

在前面閱讀源碼第5步,其實(shí)我們看過(guò),
__thrdpool_routine()里有free的地方。

于是現(xiàn)在三段魔法終于串起來(lái)了。

? 第三段魔法:嚴(yán)謹(jǐn)?shù)牟l(fā)。

static void *__thrdpool_routine(void *arg)
{ while (1) 
    {  // ... task_routine(task_context); // 如果routine里做的事情,是銷(xiāo)毀線程池... // 注意這個(gè)時(shí)候,其他內(nèi)存都已經(jīng)被destroy里清掉了,萬(wàn)萬(wàn)不可以再用什么mutex、cond if (pool->nthreads == 0) 
        { /* Thread pool was destroyed by the task. */ free(pool); return NULL;                                            
        }
        ...

非常重要的一點(diǎn),
由于并發(fā),我們是不知道誰(shuí)先操作的。
假設(shè)我們稍微改一改這個(gè)順序,
就又是另一番邏輯
。

比如我作為一個(gè)內(nèi)部線程,
routine()里調(diào)用destroy()期間,
發(fā)現(xiàn)還有線程沒(méi)有執(zhí)行完,
我就要等在我的terminate上,
待最后看到nthreads==0的那個(gè)人叫醒我。

然后,我的代碼繼續(xù)執(zhí)行,
函數(shù)棧就會(huì)從destroy()回到routine(),
也就是上面那幾行,
再然后就可以free(pool);
由于這時(shí)候我已經(jīng)放飛自我detach了,
于是一切順利結(jié)束。

你看,無(wú)論如何都可以完美地銷(xiāo)毀線程池:

并發(fā)是復(fù)雜多變的,代碼是簡(jiǎn)潔統(tǒng)一的

是不是太妙了!
我寫(xiě)到這里已經(jīng)要感動(dòng)哭了!?

? 6 - 簡(jiǎn)單的用法

這個(gè)線程池只有兩個(gè)文件:
thrdpool.h和thrdpool.c,
而且只依賴(lài)內(nèi)核的數(shù)據(jù)結(jié)構(gòu)list.h。
我們把它拿出來(lái)玩,自己寫(xiě)一段代碼:

void my_routine(void *context) { // 我們要執(zhí)行的函數(shù)  printf("task-%llu start.\n", reinterpret_cast<unsigned long long>(context); );
} void my_pending(const struct thrdpool_task *task) { // 線程池銷(xiāo)毀后,沒(méi)執(zhí)行的任務(wù)會(huì)到這里 printf("pending task-%llu.\n", reinterpret_cast<unsigned long long>(task->context););                                    
} int main() { thrdpool_t *thrd_pool = thrdpool_create(3, 1024); // 創(chuàng)建 struct thrdpool_task task; unsigned long long i; for (i = 0; i < 5; i++)
    {
        task.routine = &my_routine;                                             
        task.context = reinterpret_cast<void *>(i);                             
        thrdpool_schedule(&task, thrd_pool); // 調(diào)用 }
    getchar(); // 卡住主線程,按回車(chē)?yán)^續(xù) thrdpool_destroy(&my_pending, thrd_pool); // 結(jié)束 return 0;                                                                   
}

再打印幾行l(wèi)og,直接編譯就可以跑起來(lái):

媽媽再也不用擔(dān)心我的C語(yǔ)言作業(yè)

簡(jiǎn)單程度堪比大一上學(xué)期C語(yǔ)言作業(yè)。?

? 7 - 并發(fā)與結(jié)構(gòu)之美

最后談?wù)劯惺堋?

看完之后我很后悔為什么沒(méi)有早點(diǎn)看
為什么不早點(diǎn)就可以獲得知識(shí)的感覺(jué),
并且在淺層看懂之際,
我知道自己肯定沒(méi)有完全理解到里邊的精髓,
畢竟我不能深刻地理解到
設(shè)計(jì)者當(dāng)時(shí)對(duì)并發(fā)的構(gòu)思和模型上的選擇
。

我只能說(shuō),
沒(méi)有十多年頂級(jí)的系統(tǒng)調(diào)用和并發(fā)編程的功底
寫(xiě)不出這樣的代碼,
沒(méi)有極致的審美與對(duì)品控的偏執(zhí)
也寫(xiě)不出這樣的代碼。

并發(fā)編程有很多說(shuō)道,
就正如退出這個(gè)這么簡(jiǎn)單的事情,
想要做到退出時(shí)回收干凈卻很難。
如果說(shuō)你寫(xiě)業(yè)務(wù)邏輯自己管線程,
退出什么的sleep(1)都無(wú)所謂,
但如果說(shuō)做框架的人
不能把自己的框架做得完美無(wú)暇邏輯自洽
就難免讓人感覺(jué)差點(diǎn)意思
。

而這個(gè)thrdpool,它作為一個(gè)線程池,
是如此的邏輯完備
用最對(duì)稱(chēng)簡(jiǎn)潔的方式去面對(duì)復(fù)雜的并發(fā)。

再次讓我深深地感到震撼:
我們身邊那些原始的、底層的、基礎(chǔ)的代碼,
還有很多新思路,
還可以寫(xiě)得如此美。

Workflow項(xiàng)目源碼地址
https://github.com/sogou/workflow


本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
關(guān)閉