std :: packaged_task与std :: placeholders

时间:2019-01-05 15:20:24

标签: c++ multithreading templates c++17 variadic-functions

对代码进行简单编辑(并已解决)

我希望能够创建一个带有自由的未绑定参数的打包任务,然后在调用打包任务时将其添加。

在这种情况下,我希望函数的第一个参数(类型为size_t)不受约束。

这是一个可行的最小示例(这是解决方案):

#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>
#include <cstdlib>
#include <cstdio>

//REV: I'm trying to "trick" this into for double testfunct( size_t arg1, double arg2), take enqueue( testfunct, 1.0 ), and then internally, execute
// return testfunct( internal_size_t, 1.0 )

template<typename F, typename... Args>
auto enqueue(F&& f, Args&&... args) 
  -> std::future<typename std::result_of<F(size_t, Args...)>::type>
{
  using return_type = typename std::result_of<F(size_t, Args...)>::type;

  //REV: this is where the error was, I was being stupid and thinking this task_contents which would be pushed to the queue should be same (return?) type as original function? Changed to auto and everything worked... (taking into account Jans's result_type(size_t) advice into account.
  //std::function<void(size_t)> task_contents = std::bind( std::forward<F>(f), std::placeholders::_1, std::forward<Args>(args)... );
  auto task_contents = std::bind( std::forward<F>(f), std::placeholders::_1, std::forward<Args>(args)... );

  std::packaged_task<return_type(size_t)> rawtask(
                          task_contents );

  std::future<return_type> res = rawtask.get_future();

  size_t arbitrary_i = 10;
  rawtask(arbitrary_i);
  return res;
}


double testfunct( size_t threadidx, double& arg1 )
{
  fprintf(stdout, "Double %lf Executing on thread %ld\n", arg1, threadidx );
  std::this_thread::sleep_for( std::chrono::milliseconds(1000) );
  return 10; //true;
}

int main()
{
  std::vector<std::future<double>> myfutures;

  for(size_t x=0; x<100; ++x)
    {
      double a=x*10;
      myfutures.push_back(
              enqueue( testfunct, std::ref(a) )
                  );
    }

  for(size_t x=0; x<100; ++x)
    {
      double r = myfutures[x].get();
      fprintf(stdout, "Got %ld: %f\n", x, r );
    }
}

2 个答案:

答案 0 :(得分:1)

主要问题在 public class User { public User() { } public User(ClaimsPrincipal principal) { Id = principal.FindFirst(ClaimTypes.NameIdentifier).Value; FirstName = principal.FindFirst(ClaimTypes.GivenName).Value; Surname = principal.FindFirst(ClaimTypes.Surname).Value; Email = principal.FindFirst(ClaimTypes.Email).Value; } [Key] public string Id { get; set; } public string FirstName { get; set; } public string Surname { get; set; } public string Email { get; set; } public string Password { get; set; } public bool Active { get; set; } public DateTime CreatedDate { get; set; } public DateTime LastLoginTime { get; set; } public Company Company { get; set; } public User CreatedBy { get; set; } //Hack to get around EF/Cosmos Enum error private string UserType { get; set;} [NotMapped] public UserType UserTypeEnum { get { if (string.IsNullOrWhiteSpace(UserType)) { return Models.UserType.User; } return (UserType)Enum.Parse(typeof(UserType), UserType); } set { UserType = value.ToString(); } } } 上:

ThreadPool::enqueue

在这里,std::function<void(size_t)> task1 = std::bind( std::forward<F>(f), std::placeholders::_1, std::forward<Args>(args)... ); 的类型为task1,但是用std::function<void(std::size_t)>求值时std::bind的结果可以转换为funct,即使是@ TC指出,您可以将std::function<bool(std::size_t)>的结果分配给bind,为了将task1传递给task1,您需要兑现std::make_shared已经。

将以上内容更改为:

return_type

现在相同:

std::function<return_type(size_t)> task1 = std::bind( std::forward<F>(f), std::placeholders::_1, std::forward<Args>(args)... );

,但是在这种情况下是缺少的参数类型。更改为:

auto task = std::make_shared< std::packaged_task<return_type()> >( task1 );

auto task = std::make_shared< std::packaged_task<return_type(std::size_t)> >( task1 ); 存储类型为ThreadPool::tasks的函数对象,但是您存储的lambda不接收任何参数。将std::function<void(std::size_t)>更改为:

tasks.emplace(...)

答案 1 :(得分:1)

代码的格式不是很好,而是一种解决方案。

首先,您应该将结果包装在lambda创建中,而不是传递可以返回任何内容的函数。但是,如果要在任务上使用共享指针,则可以使用。

在原型中:

std::future<void> enqueue(std::function<void(size_t)> f);

using Task = std::function<void(size_t)>;
// the task queue
std::queue<Task> tasks;

std::optional<Task> pop_one();

实施变为:

ThreadPool::ThreadPool(size_t threads)
    :   stop(false)
{
    for(size_t i = 0;i<threads;++i)
        workers.emplace_back(
        [this,i]
            {
                for(;;)
                {
                auto task = pop_one();
                if(task)
                {
                    (*task)(i);
                }
                else break;
                }
            }
        );
}

std::optional<ThreadPool::Task> ThreadPool::pop_one()
{
    std::unique_lock<std::mutex> lock(this->queue_mutex);
    this->condition.wait(lock,
        [this]{ return this->stop || !this->tasks.empty(); });
    if(this->stop && this->tasks.empty())
    {
        return std::optional<Task>();
    }
    auto task = std::move(this->tasks.front()); //REV: this moves into my thread the front of the tasks queue.
    this->tasks.pop();

    return task;
}

template<typename T>
std::future<T> ThreadPool::enqueue(std::function<T(size_t)> fun)
{
    auto task = std::make_shared< std::packaged_task<T(size_t)> >([=](size_t size){return fun(size);});

    auto res = task->get_future();
    {
        std::unique_lock<std::mutex> lock(queue_mutex);

        // don't allow enqueueing after stopping the pool
        if(stop)
        {
            throw std::runtime_error("enqueue on stopped ThreadPool");
        }

        tasks.emplace([=](size_t size){(*task)(size);});
    }
    condition.notify_one();
    return res;
}

现在您可以拥有自己的主要产品:

int main()
{
  size_t nthreads=3;
  ThreadPool tp(nthreads);
  std::vector<std::future<bool>> myfutures;

  for(size_t x=0; x<100; ++x)
    {
      myfutures.push_back(
          tp.enqueue<bool>([=](size_t threadidx) {return funct(threadidx, (double)x * 10.0);}));
    }

  for(size_t x=0; x<100; ++x)
    {
      bool r = myfutures[x].get();
      std::cout << "Got " << r << "\n";
    }
}

现在,包装lambda时有一个显式的返回类型,因为返回类型是模板化的。