packaged_task

1. packaged_task

packaged_task类似于将一个函数任务打包,然后换到其他地方去执行。通常多线程编程就会使用的这种方式。比如简易的线程池中。
packaged_task中<>放的是函数参数类型,类似std::function中放的东西。
packaged_task的返回值是void,通常获取执行的结果是使用std::future去接受get_future,然后调用future中的get方法。

#include <future>
#include <iostream>
int FunctionOne(int x) {
    std::cout << "sub thread id : " << std::this_thread::get_id() << std::endl;
    return x; 
}

int main() {
    std::cout << "main thread id : " << std::this_thread::get_id() << std::endl;
    std::packaged_task<int(int)> tsk(FunctionOne);
    // do somthing
    tsk(666);
    std::future<int> res = tsk.get_future();
    int x = res.get();
    std::cout << x << std::endl;
}

2.

2.1

通常而言我们不能像创建thread一样,将packaged_task中函数参数传进去
但是我们可以使用std::bind将参数打包,打包参数后就需要将packaged_task<>尖括号中的参数省略,而且还是需要执行。

#include <future>
#include <iostream>
#include <functional>
int FunctionOne(int x) {
    std::cout << "sub thread id : " << std::this_thread::get_id() << std::endl;
    return x; 
}

int main() {
    std::cout << "main thread id : " << std::this_thread::get_id() << std::endl;
    std::packaged_task<int()> tsk(std::bind(FunctionOne, 666));
    tsk(); // 需要理解这个,打包任务,不代表任务执行,只有调用任务才代表任务执行了。
    std::future<int> fu = tsk.get_future();
    std::cout << fu.get() <<std::endl;
    // do somthing
} // do somthing


如果不类似tsk()函数,程序将不会正常结束,而会发生阻塞。

2.2


和future和promise一样,package_task只使用移动语义。

思考

1. std::package_task和std::async的异同?

std::async用于创建异步任务,std::packaged_task则将一个可调用对象(包括函数、函数对象、lambda表达式、std::bind表达式、std::function对象)进行包装,以便该任务能被异步调用(即在其他线程中调用)。二者均可通过std::future对象返回执行结果。二者使用的一个主要差别是:std::packaged_task需要等待执行结果返回,而std::async不必。
对于上述的第一句话怎么解释呢,就是async创建后,就会默认执行,后面直接调用get方法就好了,而packaged_task你需要自己调用。

2. 这里为什么要使用std::packaged_task,而不是直接使用std::bind?

感觉上packaged_task和std::bind没啥区别,但是package_task可以和future结合,进行异步调度。比如在在线程池中将任务打包,进行异步调用。这里举个简单例子,这段代码只是抛砖引玉.

#include <atomic>
#include <future>
#include <iostream>
#include <functional>
#include <chrono>
#include <mutex>
#include <thread>
#include <queue>
#include <condition_variable>
template <typename T>
class SafeQueue {
public:
    bool Empty() {
        std::lock_guard<std::mutex> lk(mtx_);
        return q_.empty();
    }
    void Push(T task) {
        std::unique_lock<std::mutex> lk(mtx_);
        q_.push(task);
        cv_.notify_one();
    }

    T Wait(int tm) {
        std::unique_lock<std::mutex> lk(mtx_);
        cv_.wait_for(lk, std::chrono::microseconds(tm), [&]() { return !q_.empty();});
        T t;
        if(!q_.empty()) {
           t = q_.front();
           q_.pop();
        }
        return t;
    }
private:
    std::queue<T> q_;
    std::mutex mtx_;
    std::condition_variable cv_;
}; // class SafeQueue

template<class T>
class ThreadManager {
 public:
    ThreadManager() { running_.store(true);}
    ~ThreadManager() {
        running_.store(false);
    }
    void Worker() {
        while (running_) {
            T* tsk = safe_q_.Wait(20);
            (*tsk)();
        }
    }
    void AddTask(T* tsk) {
        safe_q_.Push(tsk);
    }
 private:
    SafeQueue<T*> safe_q_;
    std::atomic<bool> running_{false};
}; // class ThreadManager

int FunctionOne(int x) {
    std::cout << x << std::endl;
    return 2 * x; 
}
int main() {
    ThreadManager<std::packaged_task<int()>> tm;
    std::thread thd = std::thread(&ThreadManager<std::packaged_task<int()>>::Worker, &tm); 
    std::packaged_task<int()> tsk(std::bind(FunctionOne, 666));
    std::future<int> fu = tsk.get_future();
    tm.AddTask(&tsk);
    std::cout << fu.get() <<std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(5));
    if(thd.joinable()) {
        thd.join();
    }
}

注意

因为这里packaged_task只支持移动语义,所以我在这写多线程时用的是指针。

posted @ 2021-08-01 15:59  cyssmile  阅读(971)  评论(2)    收藏  举报