布尔成员的C++多线程访问

C++ multithreaded access of boolean member

由于低延迟要求,我使用 for_each 的并行执行来识别句子(一组字符串)的意图。我之前了解到,不需要互斥量来保护 bool 或任何大小小于一个字节的数据类型。所以我问访问布尔成员 Tag.m_Found 是否线程安全?否则,我应该使用 atomic 还是 mutex?

#include <iostream>
#include <unordered_set>
#include <set>
#include <list>
#include <algorithm>
#include <execution>
struct Tag{
    const std::unordered_set<std::string> m_Context;
    const std::string m_Name;
    volatile bool m_Found;
    Tag(const std::unordered_set<std::string> context, const std::string name)
        : m_Context(context)
        , m_Name(name)
        , m_Found(false)
        {}
    Tag(const Tag & tag) = delete;
    Tag(Tag && tag) = default;
    Tag & operator=(const Tag & tag) = delete;
};
int main(){
    const std::set<std::string> input = {"hello", "my", "son"};
    std::list<Tag> intentions;
    intentions.emplace_back(Tag({"hello", "Hi", "morning"}, "greeting"));
    intentions.emplace_back(Tag({"father", "mother", "son"}, "family"));
    intentions.emplace_back(Tag({"car", "bus", "airplan"}, "transportation"));
    for_each( std::execution::par
            , std::begin(input)
            , std::end(input)
            , [& intentions](const std::string & input_element)
                {
                    for_each( std::execution::par
                            , std::begin(intentions)
                            , std::end(intentions)
                            , [& input_element](Tag & intention){
                                if(!intention.m_Found){
                                    intention.m_Found = intention.m_Context.find(input_element)!=intention.m_Context.end();
                                }
                            }
                        );
                }
        );
    for_each( std::execution::seq
            , std::begin(intentions)
            , std::end(intentions)
            , [](Tag & intention){
                if(intention.m_Found){
                    std::cout<<intention.m_Name;
                }
            }
        );
    return 0;
}

问题是如果添加“std::mutex m_Found_access;”或制作“atomic_bool m_Found;”默认的移动构造函数被删除,所以我需要为标签定义一个移动构造函数。并且 m_Found 应该只设置为 true 以避免竞争条件(如@Nate Eldredge所述)。代码变为:

#include <iostream>
#include <unordered_set>
#include <set>
#include <list>
#include <algorithm>
#include <execution>
struct Tag{
    const std::unordered_set<std::string> m_Context;
    const std::string m_Name;
    std::atomic_bool m_Found;
    Tag(const std::unordered_set<std::string> context, const std::string name)
        : m_Context(context)
        , m_Name(name)
        , m_Found(false)
        {}
    Tag(const Tag & tag) = delete;
    Tag & operator=(const Tag & tag) = delete;
    Tag(Tag && tag) : m_Context(std::move(tag.m_Context))
                    , m_Name(std::move(tag.m_Name))
                    , m_Found(static_cast< bool >(tag.m_Found))
                    {}
};
int main(){
    const std::set<std::string> input = {"hello", "my", "son"};
    std::list<Tag> intentions;
    intentions.emplace_back(Tag({"hello", "Hi", "morning"}, "greeting"));
    intentions.emplace_back(Tag({"father", "mother", "son"}, "family"));
    intentions.emplace_back(Tag({"car", "bus", "airplan"}, "transportation"));
    for_each( std::execution::par
            , std::begin(input)
            , std::end(input)
            , [& intentions](const std::string & input_element)
                {
                    for_each( std::execution::par
                            , std::begin(intentions)
                            , std::end(intentions)
                            , [& input_element](Tag & intention){
                                if(!intention.m_Found){
                                    if(intention.m_Context.find(input_element)!=intention.m_Context.end()){
                                        intention.m_Found = true;
                                    }
                                }
                            }
                        );
                }
        );
    for_each( std::execution::seq
            , std::begin(intentions)
            , std::end(intentions)
            , [](Tag & intention){
                if(intention.m_Found){
                    std::cout<<intention.m_Name;
                }
            }
        );
    return 0;
}