【问题标题】:Thread-safe array counter C++线程安全数组计数器 C++
【发布时间】:2021-08-26 04:31:57
【问题描述】:

我有一个包含 1000 多个元素的向量。我想获取每个元素并使用该元素发出 HTTP 请求,然后在没有多线程的情况下给我结果。它会很慢,所以我制作了多线程,每次检查大约需要 100 个元素。

我的问题是,计数器不工作,因为我计划在不检查所有元素的情况下使计数器达到最大值。

这是我的代码 sn-p:

for(int i=0; i<threads; i++){
    threadlist.push_back(thread([&]{
        while(true){
            mutex lock;
            lock.lock();
            if(counter >= Files::getUsers().size()){
                exit(0);
            }else {
                counter++;
            }
            lock.unlock();

【问题讨论】:

    标签: c++ thread-safety


    【解决方案1】:

    您似乎在每个线程中使用了单独的互斥锁。您需要在每个线程中使用相同的互斥锁才能进行任何同步。

    【讨论】:

    • 是的,对于有人告诉我这样做的那个小错误,我很抱歉,我已经知道那是错误的,但我试过了,也许它会起作用,谢谢你的帮助!
    【解决方案2】:

    您在线程内部的循环内定义lock,这意味着每个线程中的每次迭代都将有自己的互斥锁,因此您不会获得任何线程同步来保护counter。这会给您带来数据竞争,这是未定义的行为。

    您需要做的是在for 循环之外定义lock,就像您对counter 所做的那样,然后捕获互斥体以便所有线程共享它。

    或者,您可以将counter 设为std::atomic&lt;whatever_integer_type&gt;,然后您甚至不需要互斥锁,因为counter 会自行同步。

    【讨论】:

    • 是的,对于有人告诉我这样做的那个小错误,我很抱歉,我已经知道那是错误的,但我试过了,也许它会起作用,谢谢你的帮助!
    • 那个人可能是……撒旦?
    【解决方案3】:

    您不能为每个线程使用单独的互斥锁。您可以在所有线程中使用一个互斥锁(或其他一些同步原语),也可以在这种情况下使用原子值。

    使用互斥锁:

      std::vector<std::thread> threadlist;
      int counter = 0;
      std::mutex m;
      int num_threads = 8;
      for (int i = 0; i < num_threads; i++) {
          threadlist.push_back(thread([&]{
          while (true) {
            int myValue;
            {  // keep critical section minimal to avoid lock contention as much as possible
              std::lock_guard<std::mutex> lock(m);
              myValue = counter++;
            }
            if (myValue >= Files::getUsers().size()) {
              return;
            }
            //do calculation with myValue, no other thread will have the same
          }
         }));
      }
    

    使用原子

      std::vector<std::thread> threadlist;
      std::atomic<int> counter {0};
      int num_threads = 8;
      for (int i = 0; i < num_threads; i++) {
          threadlist.push_back(thread([&]{
          while (true) {
            int myValue = counter.fetch_add(1);
            if (myValue >= Files::getUsers().size()) {
              return;
            }
            //do calculation with myValue, no other thread will have the same
          }
         }));
      }
    

    【讨论】:

      【解决方案4】:

      首先将要完成的工作分成每个线程的单独向量

      首先为每个线程准备要完成的工作,这样每个线程都有自己独立的工作负载:

         const int nThreads = NUMBER_OF_THREADS;
         const int sizePerThread = Files::getUsers().size() / nThreads;
         std::vector<std::thread> threadlist;
      
         // Fills index limits for each thread
         std::vector<int> threadLimitIndex;
         for (int i=0; i<nThreads; ++i)
           threadLimitIndex.push_back(i * sizePerThread);
         threadLimitIndex.push_back(Files::getUsers().size());
      

      然后使用每个线程的限制让它们在自己的数据集上工作:

         // Does the calculation
         for (int i=0; i<nThreads; ++i)
         {
           threadlist.push_back(thread([&threadLimitIndex]{
              for (int myValue=threadLimitIndex[i]; myValue<threadLimitIndex[i+1]; ++myValue)
              {
                 // Do calculation with myValue, no other thread will have the same
              }
             }
           ));
         }
      

      不需要复杂的控制代码;-)

      CAVEAT :这是一种分离工作的简单方法,假设每个值的工作大致相同。如果每个值要完成的工作差异很大,则一些线程将提前完成并停止,而其余线程仍有工作要做,这不是最佳的。 为了保证所有线程在所有情况下都能工作到最后,您需要实现work queue

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-06-06
        • 1970-01-01
        • 1970-01-01
        • 2015-07-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多