尝试使用移动语义创建线程保护

Posted

技术标签:

【中文标题】尝试使用移动语义创建线程保护【英文标题】:Attempting to create a threadguard with move semantics 【发布时间】:2020-08-06 20:49:34 【问题描述】:

标题很容易解释。我正在尝试获得一个线程保护的最小工作示例,它还可以支持 std::threads 具有的移动语义。

#include <iostream>
#include <thread>
#include <vector>
#include <functional>

class ThreadGuard 
public:
    explicit ThreadGuard(std::thread input): t(std::move(input))
    
    ~ThreadGuard()
        if(t.joinable())
            t.join();
        
    
    ThreadGuard(ThreadGuard const& t) = delete;
    ThreadGuard& operator=(ThreadGuard const&) = delete;

    ThreadGuard& operator=(ThreadGuard&& out)
        this->t = out.transfer();
        return *this;
    
    std::thread transfer()
        return std::move(t);
    
private:
    std::thread t;
;

void doWork(std::string input)
    std::cout << input << std::endl;


static const auto numThreads = 4;
int main()

    std::vector<ThreadGuard> tp;
    tp.reserve(numThreads);
    for(auto i = 0 ; i < numThreads; ++i)
        tp[i] = ThreadGuard(std::thread(doWork, i));
    
    return 0;

目前遇到了障碍。 std::invoke,没有找到匹配的重载函数,我看不到这里缺少什么。

【问题讨论】:

【参考方案1】:

您需要将int 转换为std::string

tp[i] = ThreadGuard(std::thread(doWork, std::to_string(i)));

您也不需要编写自己的移动构造函数和移动赋值运算符。使用default

class ThreadGuard 
public:
    explicit ThreadGuard(std::thread&& input): t(std::move(input))
    
    ThreadGuard(ThreadGuard const& t) = delete;
    ThreadGuard(ThreadGuard&&) noexcept = default;
    ThreadGuard& operator=(ThreadGuard const&) = delete;
    ThreadGuard& operator=(ThreadGuard&&) noexcept = default;  
    ~ThreadGuard()
        if(t.joinable())
            t.join();
        
    

private:
    std::thread t;
;

您还可以让转换构造函数接受线程构造函数参数并直接转发它们:

    template<typename...Args>
    explicit ThreadGuard(Args&&... args): t(std::forward<Args>(args)...)
    

这将允许它像这样创建:

tp[i] = ThreadGuard(doWork, std::to_string(i));

另外值得注意的是:在 C++20 中添加了 std::jthread,而 join()s 在销毁时自动添加。

【讨论】:

ThreadGuard&amp; operator=(ThreadGuard&amp;&amp;) noexcept = default; 我觉得不对,你自己写吧。您需要检查this-&gt;t 是否可加入(并加入),然后再覆盖它。 @MikeVine 在这种情况下,默认行为(OP 也有)将调用std::terminate。它可能是也可能不是 OP 想要的,但我只是在答案中保持了相同的逻辑。 是的 OP 也有这个错误 (?)。这将是一个 非常 奇怪的类,在这种情况下不会自行清理。它很容易修复 - 或者如果你真的想在那种情况下终止,那么明确似乎是正确的。 @MikeVine 我想说的是品味问题。这个瘦包装器的行为与std::thread 一样,只是它在销毁时自动加入。另一方面,std::jthread 有一个更智能的移动赋值运算符,它调用request_stop(),然后调用join()。对于类似于使用std::thread 的东西,需要添加一个需要检查的原子。只是join()ing 可能会导致各种麻烦。 1+ 以获得完整答案。

以上是关于尝试使用移动语义创建线程保护的主要内容,如果未能解决你的问题,请参考以下文章

c++,如何创建线程受限/受保护的变量和函数?

如何创建线程?如何保证线程安全?

并发编程-锁相关的内存语义

将互斥保护构建到 C++ 类中的线程安全方法?

锁的内存语义

java并发学习--第十章 java内存模型的内存语义