【问题标题】:Inserting thread::id and class instance in a map在映射中插入 thread::id 和类实例
【发布时间】:2021-07-30 19:45:20
【问题描述】:

我有一堂课:

class IOWorker {

std::thread                                    thread_;
boost::asio::io_service                        ios_;
boost::optional<boost::asio::io_service::work> work_;
Callback                                       callback_;

// Map of thread::id and this class' instance
typedef std::map<std::thread::id, IOWorker *>  IOWorkerThreadMap;
static IOWorkerThreadMap                       iOWorkerThreadMap;

public:
    IOWorker(Callback cb);
    ~IOWorker();
    
    std::thread::id getThreadId() {
       return thread_.get_id();
    }
    
    // IO worker threads will call this to fetch their instance
    static IOWorker* getIOWorkerInstance (void) {
       auto it = iOWorkerThreadMap.find(std::this_thread::get_id());
       if (it == iOWorkerThreadMap.end()) {
           return nullptr;
       }
       return (it->second);
    }
};

IOWorker::IOWorker (Callback cb) : callback_{cb}
{
    work_ = boost::in_place(boost::ref(ios_));
    thread_ = std::thread{[this] () {
               ios_.run();
                  }
           };
}

在主线程执行的函数中,我创建了 10 个此类的实例并将它们插入到映射中,其中 thread::id 是键,class 实例是值。 我正在从所有工作线程访问此映射,以通过在映射中查找它们的 thread::id 来获取它们各自的 class 实例。主线程也访问这些实例,调用一些方法,在 ios_ 上发布作业等。

void startIOWorkers (Callback cb)
{
   for (int i = 0; i < 10; ++i) {
       IOWorker *iow = new IOWorker{cb};
       std::thread::id threadId = iow->getThreadId();
       IOWorkerThreadMap.insert(std::make_pair(threadId, iow));
   }
}

我的问题是针对以下行:

IOWorkerThreadMap.insert(std::make_pair(threadId, iow));

我的理解(可能是错误的!)是上述函数中的iowthreadId 在我将它们插入地图时将被“复制”,并且它们的两个副本将存在。

我想避免这种情况,因此,我想知道在这种情况下拥有thread::idclass 实例的映射的更好方法是什么?

【问题讨论】:

  • 是我遗漏了什么,还是仅仅使用thread_local IOWorker* this_thread_worker; 会简单得多?
  • 您的代码没有显示同步,以确保两个或多个线程不会同时访问静态地图。
  • @PaulMcKenzie,它在原始代码中得到了照顾。删除它以减少“示例”中的混乱。甚至类定义也大大缩短了。
  • 那么我不明白为什么创建地图条目会是一个问题。如果一切都是同步的,我看不出你声称的任何问题可能是一个问题。那么,如果制作了副本会怎样(这就是基于价值的容器的工作方式)。
  • 当然会有两份。但是,如果本地版本无论如何都会超出范围,会有什么问题呢?对于在映射、向量、std::list 等中插入值的任何 C++ 代码,您不妨提出此问题。

标签: c++ stdmap


【解决方案1】:

这似乎比它必须的要复杂得多。

如果看起来确实如此,您只需从thread::this_thread 访问地图,该语言已经内置了map&lt;thread_id, T&gt;thread_local

class IOWorker {

std::thread                                    thread_;
boost::asio::io_service                        ios_;
boost::optional<boost::asio::io_service::work> work_;
Callback                                       callback_;

static thread_local IOWorker*                  this_thread_worker_;

public:
    IOWorker(Callback cb);
    ~IOWorker();
    
    // IO worker threads will call this to fetch their instance
    static IOWorker* getIOWorkerInstance (void) {
       return this_thread_worker_;
    }
};

IOWorker::IOWorker (Callback cb) : callback_{std::move(cb)}
{
    work_ = boost::in_place(boost::ref(ios_));
    thread_ = std::thread{[this] () {
       this_thread_worker_ = this;
       ios_.run();
    };
}


std::vector<IOWorker> startIOWorkers(Callback cb)
{
    std::vector<IOWorker> launched_workers;
    launched_workers.reserve(10);
    
    for (int i = 0; i < 10; ++i) {
       launched_workers.emplace_back(cb);
    }

    return launched_workers;
}

【讨论】:

  • 实际上,在一种情况下,主线程也访问这些实例以发布一些工作给线程做。因此我想到了维护上述地图。
  • 所以,是的,主线程也想访问这些实例,调用一些方法,在 ios_ 上发布作业等。我们如何使用您的解决方案来实现呢?
  • @void 主线程可以只保留一个指向工人的指针容器。具有工作人员列表的主线程和工作线程能够获得指向各自IOWorker对象的指针是不相关的功能,可以单独处理。
  • @void 我已经用主线程如何跟踪启动的工作人员的示例更新了答案。无论如何都需要管理这些对象的生命周期,因此无论如何您都必须在某个时候这样做,
  • @void 当向量被销毁时,IOWorkers 将与它一起被销毁。字面意思就是这么简单。只需保留矢量,直到摧毁工人。几乎没有理由再使用 new/delete 了。
猜你喜欢
  • 2019-03-19
  • 1970-01-01
  • 2020-01-27
  • 2018-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多