簡體   English   中英

c ++線程池:用於將函數/ lambdas傳遞給線程的std :: function的替代方法?

[英]c++ thread pool: alternative to std::function for passing functions/lambdas to threads?

我有一個線程池,我用它來執行許多小工作(數百萬個工作,每個幾十個/幾百毫秒)。 工作以以下任何一種形式傳遞:

std::bind(&fn, arg1, arg2, arg3...)

要么

[&](){fn(arg1, arg2, arg3...);}

使用線程池將它們像這樣:

std::queue<std::function<void(void)>> queue;

void addJob(std::function<void(void)> fn)
{
    queue.emplace_back(std::move(fn));
}

非常標准的東西....除了我注意到瓶頸,如果作業在足夠快的時間內執行(小於一毫秒),在addJob函數中從lambda / binder到std :: function的轉換實際上需要比執行工作本身。 在做了一些閱讀之后,std :: function非常慢,因此我的瓶頸並不一定是出乎意料的。

有沒有更快的方式來做這種事情? 我已經研究了drop-in std :: function替換,但它們或者與我的編譯器不兼容或者速度不快。 我也研究了Don Clugston的“快速代表”,但他們似乎不允許將參數與函數一起傳遞(也許我不能正確理解它們?)。

我正在使用VS2015u3進行編譯,傳遞給作業的函數都是靜態的,它們的參數是ints / floats或指向其他對象的指針。

為每個任務類型都有一個單獨的隊列 - 您可能沒有成千上萬的任務類型 這些中的每一個都可以是例如任務的靜態成員。 然后addJob()實際上是Task的ctor,它是完全類型安全的。

然后定義任務類型的編譯時列表,並通過模板元編程(for_each)訪問它。 它會更快,因為你不需要任何虛擬調用fnptr / std::function<>來實現這一點。

這只有在您的元組代碼看到所有Task類時才會起作用(因此您不能通過從光盤加載圖像來向已經運行的可執行文件添加Task的新后代 - 希望這不是問題)。

template<typename D> // CRTP on D
class Task {
public:
    // you might want to static_assert at some point that D is in TaskTypeList

    Task() : it_(tasks_.end()) {} // call enqueue() in descendant

    ~Task() {
        // add your favorite lock here
        if (queued()) {
            tasks_.erase(it_);
        }
    }

    bool queued() const { return it_ != tasks_.end(); }

    static size_t ExecNext() {
        if (!tasks_.empty()) {
            // add your favorite lock here
            auto&& itTask = tasks_.begin();
            tasks_.pop_front();
            // release lock
            (*itTask)();
            itTask->it_ = tasks_.end();
        }
        return tasks_.size();
    }

protected:
    void enqueue() const
    {
        // add your favorite lock here
        tasks_.push_back(static_cast<D*>(this));
        it_ = tasks_.rbegin();
    }

private:
    std::list<D*>::iterator it_;

    static std::list<D*> tasks_; // you can have one per thread, too - then you don't need locking, but tasks are assigned to threads statically
};

struct MyTask : Task<MyTask> {
    MyTask() { enqueue(); } // call enqueue only when the class is ready
    void operator()() { /* add task here */ }
    // ...
};

struct MyTask2; // etc.

template<typename...>
struct list_ {};

using TaskTypeList = list_<MyTask, MyTask2>;

void thread_pocess(list_<>) {}

template<typename TaskType, typename... TaskTypes>
void thread_pocess(list_<TaskType, TaskTypes...>)
{
    TaskType::ExecNext();
    thread_process(list_<TaskTypes...>());
}

void thread_process(void*)
{
    for (;;) {
        thread_process(TaskTypeList());
    }
}

調整這段代碼有很多東西:不同的線程應該從隊列的不同部分開始(或者一個人會使用一個環,或者幾個隊列以及靜態/動態分配給線程),當有空的時候你會把它發送到睡眠狀態。絕對沒有任務,可以有任務的枚舉等。

請注意,這不能與任意lambdas一起使用:您需要列出任務類型。 你需要在聲明它的函數中“傳遞”lambda類型(例如,通過返回`std :: make_pair(retval,list_),有時候這並不容易。但是,你總是可以將lambda轉換為functor,很簡單 - 只是丑陋。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM