1. 程式人生 > >mysql線程池的實現原理淺析

mysql線程池的實現原理淺析

new one lose clear pre turn logs color 否則

今天抽空主要看了一下mysql線程池(cached threads)的實現原理,總體並不那麽復雜,也學到了一些設計原理,值得記錄一下。為了簡化代碼,讓思路更清晰,我刪去了不少錯誤處理,線程同步鎖的代碼,mysql中大量使用全局變量,這些都需要鎖了控制訪問。

先大致說一下幾個關鍵的東西:

1、List結構:這個看名字就知道,是一個list,可以理解為隊列,這個數據結構是用來放thd的,就是線程數據的,這裏不去深究list如何實現了(暫時)。

2、threads:一個thd的list,這些thd都是活躍線程(wake thread)。

3、cached_threads:也是一個thd的list,但並不活躍,實際上是一個等待thd,即如果當前有數據在裏面,且有活躍線程結束,那麽這個走進結束流程的線程會取出thd,然後運行這個thd,這樣就不用新起線程了。

首先看看創建線程的代碼:

void create_new_thread(thd){
    // 檢查連接數是否過大
    mysql_mutext_lock(&lock_connection_count);
    if (connetion_count >= max_connections + 1 || abort_loop){
        mysql_mutext_unlock(&lock_connection_count);
        delete thd; // will close the connection
        return;
    }
    
++connection_count; if (connection_count > max_used_connections){ max_used_connections = connection_count; } mysql_mutext_unlock(&lock_connection_count); mysql_mutex_lock(&lock_thread_count); // 我在想如果連接很多很多,溢出了怎麽辦?? thd->thread_id = thd->variables.pseudo_thread_id = thread_id++; thread_count
++; // add_connection當然是個函數指針,thread_scheduler是一個放了這些函數指針的結構體 // 為了跨平臺,卻沒有使用C++的多態,而是用了函數指針實現了多態 thread_scheduler->add_connection(thd); }
// 這個就是add_connection的實現
void create_thread_to_handle_connection(thd){
    // 註意這個判斷,cache_thread_count大於wake_thread的時候才會進來,
    // 當線程結束的時候,線程cachd_thread_count++
    // 當發現有空閑,先wake_thread_count++,cachd_thread_count--
    // 成功啟動線程後,wake_thread_count--,這時wake_thread_count和cachd_thread_count又歸於平衡
    // 簡單來說,cache_thread_count是用來記錄可用線程數量的,而wake_thread是用來記錄就緒線程數量的
    // 多數時候,wake_thread_count基本是0,而cache_thread_count則大於等於0
    if (cache_thread_count > wake_thread){
        thread_cache.push_back(thd);    
        wake_thread++;
        mysql_cond_signal(&cond_thread_cache);
    }
    else {
        // 如果沒有cached thread則直接創建新的線程
        thread_created++;
        // threads這個list放著所有正在運行時的thd
        threads.append(thd);    
        thd->prior_thr_create_utime = thd->start_utime = my_micro_time();
        // 真正創建一個線程出來
        if (error = mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib
                    handle_one_connection)) {
            // do some clear up
            // ignore the lock operations
            // 創建失敗清理一下
            thread_count--;
            connection_count--;
            delete thd;
        }
    }
}

那麽什麽時候會進入if (connetion_count >= max_connections + 1 || abort_loop)這個if語句當中呢?就是有線程結束,並等待新的thd來的時候,看看線程結束的時候做了些什麽:

// 線程結束時的回調
bool one_thread_per_connection_end(thd, bool put_in_cache){
    unlink_thd(thd); // delete thd
    if (put_in_cache) {
        // 關鍵函數cache_thread
        put_in_cache = cache_thread();
    }
    // 如果已經獲得新thd,就不結束了
    if (put_in_cache){
        return 0;
    }
    
    // 否則結束線程
    my_thread_end();
    mysql_cond_broadcast(&cond_thread_count);

    pthread_exit(0);
    return 0;
}

其中的cache_thread是關鍵函數:

// 這個函數就是cache thread的關鍵,他的思路就是線程結束的時候,wait for cache_thread隊列
// 取出新的thd,運行之
static bool cache_thread(){
    // 判斷是否到達cache 線程的閾值
    if (cache_thread_count < thread_cache_size && !abort_loop && !kill_cache_threads){
        cache_thread_count++; // 進入cache狀態    

        // wait for the signal to relive the thread
        // 開始wait for新的thd裝進cache_thread list
        // 註意這裏用的是while而不是if,因為
        // 1、cond_wait可能虛假喚醒,可能因為競爭,並沒有真正獲得新的thd
        // 2、若獲取失敗則再次等新的thd,總之就是盡可能的獲得新的thd
        while (!abort_loop && !wake_thread && !kill_cached_threads){
            mysql_cond_wait(&cond_thread_cache, &lock_thread_count);    
            cached_thread_count--;
            // 此處沒仔細看是什麽邏輯,暫時跳過
            if (kill_cached_threads){
                mysql_cond_signal(&cond_flush_thread_cache);    
            }
            // 發現有就緒的thd,獲取之,然後執行
            if (wake_thread){
                THD* thd;
                wake_thread--;
                thd = thread_cache.get();        
                thd->thread_stack = (char*)&thd;
                thd->store_globals();

                thd->mysys_var->abort = 0;
                thd->prior_thr_create_utime = thd->start_utime = my_micro_time();
                // 返回1使得線程不會結束,跑新的thd了
                return 1;
            }
        }
    }
    // 結束線程
    return 0;
}

分析cache thread思路完畢。

mysql線程池的實現原理淺析