使用具有共享全局变量的线程

Posted

技术标签:

【中文标题】使用具有共享全局变量的线程【英文标题】: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)使用原子类型和操作来避免对锁的需要。 【参考方案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),因为每个线程都在对整个数组求和。如果您想使用线程对数组求和,则需要以某种方式分解数组以告诉每个线程要求和的部分。

以上是关于使用具有共享全局变量的线程的主要内容,如果未能解决你的问题,请参考以下文章

如何在php中跨线程共享全局变量?

一起学Python: 多线程-共享全局变量问题

多线程-共享全局变量

一起学Python:多线程-共享全局变量

线程(调用的两种方法全局变量共享线程数量)

线程之间共享全局变量存在问题