天天看點

C++多線程之旅實戰-線程池的了解

前言

在很多公司小組都需要使用會議室進行讨論,但是每個小組都配備一個會議室又會很浪費。是以就将所有的會議室都拿出來放到一起,那個小組需要就像行政部門申請即可,根據申請的先後順序使用會議室。使用完成以後自動歸還,無需派專人進行管理。

基于這一思路,我們也可以把線程資源放到一個區域,然後根據每個使用者的需求配置設定線程資源。并且還可以實作自動化的線程資源配置設定。

設計線程池有幾個關鍵的問題:第一,線程中應該建立幾個工作線程;第二,是否應該等待線程執行結束…

第一個線程

C++多線程之旅實戰-線程池的了解

submit

不斷送出任務,然後threads擷取其中任務然後執行。這就是線程池的主要執行原理。

#include <thread>
#include <atomic>
#include <queue>
#include <vector>
#include <iostream>
class join_threads
{
    std::vector<std::thread> &threads;
public:
    explicit join_threads(std::vector<std::thread> &threads_):threads(threads_){}
    ~join_threads()
    {
        for(unsigned long i = 0 ; i < threads.size();++i)
        {
            if(threads[i].joinable())
                threads[i].join();
        }
    }
};

class thread_pool {
    std::atomic_bool done;
    std::queue<std::function<void()> > work_queue;
    std::vector<std::thread> threads;
    join_threads joiner;

    void work_thread() {
        while (!done) {
            std::function<void()> task;
            task = work_queue.front();
            work_queue.pop();
            if (task) {
                task();
            } else {
                std::this_thread::yield();
            }
        }
    }
public:
    thread_pool() : done(false), joiner(threads) {
        unsigned const thread_count = std::thread::hardware_concurrency();

        try {
            for (unsigned i = 0; i < thread_count; ++i) {
                threads.push_back(std::thread(&thread_pool::work_thread, this));
            }
        } catch (...) {
            done = true;
            throw;
        }
    }

    ~thread_pool() {
        done = true;
    }

    template<typename FunctionType>
    void submit(FunctionType f) {
        work_queue.push(std::function<void() >(f));
    }
};

void fun() {
    std::cout << "hello world" << std::endl;
}

int main() {
    thread_pool pool;
//    std::cout << "123";
    pool.submit(fun);
    pool.submit(fun);
    pool.submit(fun);
    pool.submit(fun);
    pool.submit(fun);
    pool.submit(fun);
    pool.submit(fun);
    pool.submit(fun);
    pool.work_thread();
}
           

這就是簡單實作的線程池,通過

submit()

送出資料,調用

work_thread()

函數執行。

這就類似每天晚上小組送出第二天會議室申請表,然後到了第二天直接配置設定會議室使用權。這樣就可以實作,例如在一些搶票或者需要快速反應的多線程情況下,就可以使用線程池。

thread_pool() : done(false), joiner(threads) {
	unsigned const thread_count = std::thread::hardware_concurrency();
	try {
		for (unsigned i = 0; i < thread_count; ++i) {
			threads.push_back(std::thread(&thread_pool::work_thread, this));
		}
	} catch (...) {
		done = true;
		throw;
	}
}
           

根據目前的處理的線程數,建立線程,将這一線程放入到

threads

容器中。利用

done

訓示是否線程池完成操作,當

done

為false時,暫未完成線程池,為true時則完成線程池。

void work_thread() {
	while (!done) {
		std::function<void()> task;
		task = work_queue.front();
		work_queue.pop();
		if (task) {
			task();
		} else {
			std::this_thread::yield();
		}
	}
}
           

work_thread()

從隊列中取出待執行的函數,然後執行函數。

但是上面這個函數我也不知道為什麼始終沒辦法編譯通過,可以去看這位同僚的部落格。

如果大家能夠告訴我我的代碼那個地方存在問題我将萬分感謝!

避免乒乓緩存

每次線程調用

submit

時,會向共享隊列添加一個新元素,類似的工作線程會不停的從隊列中取出元素來執行,這意味着處理器數目的增加會導緻工作隊列競争。

避免的方法主要就是給每一個線程都使用一個單獨的工作隊列,将任務放到自己的隊列中。

總結

這一部分内容主要需要動手實作,雖然在很多語言中線程池已經寫成子產品直接調用執行了。但是還是要知道其中的原理,以免在面試中遇到這樣的問題,搞得瓜兮兮的!