【问题标题】:using threads with a shared global variable使用具有共享全局变量的线程
【发布时间】:2015-12-03 23:41:04
【问题描述】:

当线程访问共享全局变量时,我无法想出一种算法来确保互斥。我正在尝试编写一个可以使用全局变量的线程函数,而不是将其切换为局部变量。

到目前为止我有这个代码:

int sumGlobal = 0;

void sumArray(int** array, int size){

  for (int i=0; i<size; i++){
    for (int j=0; j<size; j++){
      sumGlobal += array[i][j];
    }
  }
}

int main(){
    int size = 4000;
    int numThreads = 4;

    int** a2DArray = new int*[size];
    for (int i=0; i<size; i++){
        a2DArray[i] = new int[size];
        for (int j=0; j<dim; j++){
          a2DArray[i][j] = genRandNum(0,100);
        }    
    }


    std::vector<std::future<void>> thread_Pool;
    for (int i = 0; i < numThreads; ++i) {

        thread_Pool.push_back( std::async(launch::async,
                        sumArray, a2DArray, size));
    }
}

我不确定如何保证sumGlobal 不会被每个线程重写。我想正确更新它,以便每个线程在完成时将其值添加到全局变量中。我只是想学习线程,而不是局限于非空函数。

【问题讨论】:

  • 您有两个选择:1)使用锁进行互斥。 2)使用原子类型和操作来避免对锁的需要。

标签: c++ multithreading


【解决方案1】:

制作变量atomic

#include <atomic>
... 
std::atomic<int> sumGlobal {0};

原子变量不受数据竞争的影响:即使有多个线程尝试读取和写入它,它也表现良好。原子性是通过互斥还是无锁方式实现的,由实现来决定。当您使用 += 以原子方式更新变量时,您的示例中不存在不一致的风险。

nice video 更详细地向您解释什么是原子,为什么需要它们,以及它们是如何工作的。

您也可以保持变量原样并使用互斥锁/lock_gard 来保护它,正如我的@Miles Budnek 所解释的那样。问题是,一次只有一个线程可以执行受互斥锁保护的代码。在您的示例中,这意味着不同线程中的处理不会真正同时进行。原子方法应该具有卓越的性能:一个线程可能仍然计算索引并读取数组,而另一个线程正在更新全局变量。

【讨论】:

  • 谢谢,虽然不是我想要的解决方案。您是否知道在保持变量不变的同时实现这一点的方法?虽然在旁注中,但在尝试了您的建议后,我得到了编译错误。不知道为什么。
  • @foolsgold 这是一个很小的更改,可能是解决您问题的最佳解决方案。为什么它不是您所追求的“解决方案”?
  • @foolsgold 我已经更新了初始化以消除所有编译器上的错误消息。我已经完成了互斥锁以允许更长时间地冻结变量的值,但我不会在您的示例中推荐这种方法。
  • 感谢视频,非常有趣!
【解决方案2】:

如果您不想像@Christophe 建议的那样使用std::atomic&lt;int&gt; 之类的同步对象,您可以使用std::mutexstd::lock_guard 手动同步对总和的访问。

int sumGlobal = 0;
std::mutex sumMutex;

void sumArray(...) {
    for(...) {
        for(...) {
            std::lock_guard<std::mutex> lock(sumMutex);
            sumGlobal += ...;
        }
    }
}

请记住,所有锁定和解锁都会产生相当多的开销。

【讨论】:

  • 感谢您的回复,这些方法很有帮助。只是让你知道,这不会编译,error: missing template arguments before 'lock.' 我通过添加&lt;std::mutex&gt; 来编译它,但结果不正确。与非线程版本相比,这会产生不正确的 sumGlobal 值。如果需要,我可以包含非线程版本。
  • 糟糕,修复了缺少的模板参数。请注意,我只是复制了该函数,因为这不是您要问的,但它不会产生数组的总和。 sumGlobal 最终将成为您当前代码的numThreads * sum(a2DArray),因为每个线程都在对整个数组求和。如果您想使用线程对数组求和,则需要以某种方式分解数组以告诉每个线程要求和的部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-23
  • 2021-08-14
  • 2016-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-19
相关资源
最近更新 更多