草庐IT

c++ - 将转发引用传递给线程包装器类

coder 2024-02-04 原文

我们先来看一下具体的功能:

void boo1 () { std::cout << "boo1\n"; }
void boo2 (std::string) { std::cout << "boo2\n"; }

struct X {
    void boo3 () { std::cout << "boo3\n"; }
    void boo4 (std::string) { std::cout << "boo4\n"; }
};

我希望这些功能与一些“守卫”功能一起执行,该功能可以进行异常保护(下面的解释)。所以,我写了两个函数,一个是自由函数,另一个是成员函数:

template <typename C, typename... Args>
typename std::enable_if<!std::is_member_function_pointer<C>::value, void>::type
foo (C&& c, Args&&... args) {
    std::cout << "not memfun\n";
    c (std::forward<Args> (args)...);
}

template <typename C, typename T, typename... Args>
typename std::enable_if<std::is_member_function_pointer<C>::value, void>::type
foo (C&& c, T* o, Args&&... args) {
    std::cout << "memfun\n";
    (o->*c) (std::forward<Args> (args)...);
}

现在我要执行 boo1()boo2()X::boo3()X: :boo4() 在新线程中,但具有 foo() 函数使用的“保护”。所以,没有任何问题,我可以这样做:

X x;
std::thread th1 {&foo<void (*) ()>, &boo1};
std::thread th2 {&foo<void (*) (std::string), std::string>, &boo2, "Hello"};
std::thread th3 {&foo<void (X::*) (), X>, &X::boo3, &x};
std::thread th4 {&foo<void (X::*) (std::string), X, std::string>, &X::boo4, &x, "Hello"};

因此,更进一步,我编写了线程包装器类以停止显式使用 std::join()std::detach()。 Insted 其中之一在析构函数运行时被调用。为了本示例的简单性,我只使用了其中一个。 因此,代码如下所示:

class Th {
public:
    template <typename C, 
              typename = typename std::enable_if<!std::is_member_function_pointer<C>::value, void>::type,
              typename... Args>
    Th (C&& c, Args... args) // (X)
    : t {foo<C, Args...>, std::forward<C> (c), std::forward<Args> (args)...} {}

    template <typename C,
              typename = typename std::enable_if<std::is_member_function_pointer<C>::value, void>::type,
              typename T,
              typename... Args>
    Th (C&& c, T* o, Args... args) // (Y)
    : t { foo<C, T, Args...>, std::forward<C> (c), o, std::forward<Args> (args)...} {}


    ~Th () { t.join (); }

private:
    std::thread t;
};

而且用这个实现是没有问题的,下面的使用案例:

X x;
Th h1 {&boo1};
Th h2 {&boo2, "Hello"};
Th h3 {&X::boo3, &x};
Th h4 {&X::boo4, &x, "Hello"};

按异常(exception)情况工作。然而这里有一个缺陷——没有完美的转发。如果我将 (X)(Y) Args... 更改为 Args&&... 这是想要的, 代码未编译时出现一些错误,其中之一是:

error: no type named ‘type’ in ‘class std::result_of<void (*(void (*)(std::basic_string<char>), const char*))(void (*&&)(std::basic_string<char>), const char (&)[6])>’

如果我理解正确,std::thread 构造函数中的 std::bind() 无法完成它的工作,如果我错了请纠正我。

我的问题是:在这种情况下,我该怎么做才能利用完美转发?

用于编译它的编译器是 GCC 4.8.1。

promise 的解释:我希望 foo() 函数用 try 包围对 c() 的调用,并且catch 以确保捕获到异常,以某种日志记录机制处理此信息,并在需要时重新抛出异常,并且对 Th 类的用户透明。

最佳答案

将左值引用传递给 std::thread 时,您需要将它们包装在 std::reference_wrapper 中。例如,您可以通过以下方式进行:

namespace detail
{

template<typename T>
std::reference_wrapper<T> forward_to_thread(T& t, std::true_type) {
    return std::ref(t);
}

template<typename T>
T&& forward_to_thread(T&& t, std::false_type) {
    return std::move(t);
}

}

template<typename T>
auto forward_to_thread(T&& t)
    -> decltype(detail::forward_to_thread(std::forward<T>(t), std::is_lvalue_reference<T>{}))
{
    return detail::forward_to_thread(std::forward<T>(t), std::is_lvalue_reference<T>{});
}

然后你只需在 thread 初始化中替换 std::forwardforward_to_thread 的调用:

class Th {
public:
    template <typename C,
              typename = typename std::enable_if<!std::is_member_function_pointer<C>::value, void>::type,
              typename... Args>
    Th (C&& c, Args&&... args) // (X)
    : t {foo<C, Args...>, std::forward<C> (c), forward_to_thread<Args> (std::forward<Args>(args))...} {}

    template <typename C,
              typename = typename std::enable_if<std::is_member_function_pointer<C>::value, void>::type,
              typename T,
              typename... Args>
    Th (C&& c, T* o, Args&&... args) // (Y)
    : t { foo<C, T, Args...>, std::forward<C> (c), o, forward_to_thread<Args> (std::forward<Args>(args))...} {}


    ~Th () { t.join (); }

private:
    std::thread t;
};

这确实适用于 gcc 4.8.2 [ link ]

如果您可以使用更新的编译器,请考虑使用 std::invoke (C++17) 以获得更简洁和可读的代码(即 this )。

关于c++ - 将转发引用传递给线程包装器类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47349533/

有关c++ - 将转发引用传递给线程包装器类的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  3. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

  4. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

  5. ruby-on-rails - Cucumber 是否只是 rspec 的包装器以帮助将测试组织成功能? - 2

    只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您

  6. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

  7. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  8. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  9. ruby - 如何让Ruby捕获线程中的语法错误 - 2

    我正在尝试使用ruby​​编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?

  10. ruby - 如何在 ruby​​ 中运行后台线程? - 2

    我是ruby​​的新手,我认为重新构建一个我用C#编写的简单聊天程序是个好主意。我正在使用Ruby2.0.0MRI(Matz的Ruby实现)。问题是我想在服务器运行时为简单的服务器命令提供I/O。这是从示例中获取的服务器。我添加了使用gets()获取输入的命令方法。我希望此方法在后台作为线程运行,但该线程正在阻塞另一个线程。require'socket'#Getsocketsfromstdlibserver=TCPServer.open(2000)#Sockettolistenonport2000defcommandsx=1whilex==1exitProgram=gets.chomp

随机推荐