简体   繁体   English

线程安全的无锁阵列

[英]Thread-safe lock-free array

I have a C++ library, which supposed to do some computations on multiple threads. 我有一个C ++库,它应该在多个线程上进行一些计算。 I made independent threads code (ie there are no shared variables between them), except for one array. 我创建了独立的线程代码(即它们之间没有共享变量),除了一个数组。 The problem is, I don't know how to make it thread-safe. 问题是,我不知道如何使其成为线程安全的。

I looked at mutex lock/unlock ( QMutex , as I'm using Qt), but it doesn't fit for my task - while one thread will lock the mutex, other threads will wait! 我看了互斥锁/解锁( QMutex ,因为我正在使用Qt),但它不适合我的任务 - 一个线程将锁定互斥锁,其他线程将等待!

Then I read about std::atomic , which looked like exactly what I needed. 然后我读到了std::atomic ,它看起来就像我需要的那样。 Nevertheless, I tried to use it in the following way: 不过,我尝试以下列方式使用它:

std::vector<std::atomic<uint64_t>> *myVector;

And it produced compiler error ( use of deleted function 'std::atomic::atomic(const std::atomic&)' ). 并且它产生了编译器错误( 使用已删除的函数'std :: atomic :: atomic(const std :: atomic&)' )。 Then I found the solution - use special wrapper for std::atomic . 然后我找到了解决方案 - 为std::atomic使用特殊包装器。 I tried this: 我试过这个:

struct AtomicUInt64
{
    std::atomic<uint64_t> atomic;

    AtomicUInt64() : atomic() {}

    AtomicUInt64 ( std::atomic<uint64_t> a ) : atomic ( atomic.load() ) {}

    AtomicUInt64 ( AtomicUInt64 &auint64 ) : atomic ( auint64.atomic.load() ) {}

    AtomicUInt64 &operator= ( AtomicUInt64 &auint64 )
    {
                atomic.store ( auint64.atomic.load() );
    }
};

std::vector<AtomicUInt64> *myVector;

This thing compiles succesfully, but when I can't fill the vector: 这个东西成功编译,但是当我无法填充向量时:

myVector = new std::vector<AtomicUInt64>();

for ( int x = 0; x < 100; ++x )
{
    /* This approach produces compiler error:
     * use of deleted function 'std::atomic<long long unsigned int>::atomic(const std::atomic<long long unsigned int>&)'
     */
    AtomicUInt64 value( std::atomic<uint64_t>( 0 ) ) ;
    myVector->push_back ( value );

    /* And this one produces the same error: */
    std::atomic<uint64_t> value1 ( 0 );
    myVector->push_back ( value1 );
}

What am I doing wrong? 我究竟做错了什么? I assume I tried everything (maybe not, anyway) and nothing helped. 我假设我尝试了一切(也许不是,但无论如何)并没有任何帮助。 Are there any other ways for thread-safe array sharing in C++? 在C ++中有没有其他方法可以进行线程安全的数组共享?

By the way, I use MinGW 32bit 4.7 compiler on Windows. 顺便说一下,我在Windows上使用MinGW 32bit 4.7编译器。

Here is a cleaned up version of your AtomicUInt64 type: 以下是AtomicUInt64类型的清理版本:

template<typename T>
struct MobileAtomic
{
  std::atomic<T> atomic;

  MobileAtomic() : atomic(T()) {}

  explicit MobileAtomic ( T const& v ) : atomic ( v ) {}
  explicit MobileAtomic ( std::atomic<T> const& a ) : atomic ( a.load() ) {}

  MobileAtomic ( MobileAtomic const&other ) : atomic( other.atomic.load() ) {}

  MobileAtomic& operator=( MobileAtomic const &other )
  {
    atomic.store( other.atomic.load() );
    return *this;
  }
};

typedef MobileAtomic<uint64_t> AtomicUInt64;

and use: 并使用:

AtomicUInt64 value;
myVector->push_back ( value );

or: 要么:

AtomicUInt64 value(x);
myVector->push_back ( value );

your problem was you took a std::atomic by value, which causes a copy, which is blocked. 你的问题是你拿了一个std::atomic的值,这会导致一个被阻止的副本。 Oh, and you failed to return from operator= . 哦,你没能从operator=返回。 I also made some constructors explicit, probably needlessly. 我也做了一些明确的构造函数,可能是不必要的。 And I added const to your copy constructor. 我将const添加到你的拷贝构造函数中。

I would also be tempted to add store and load methods to MobileAtomic that forwards to atomic.store and atomic.load . 我也很想将storeload方法添加到MobileAtomic ,然后转发到atomic.storeatomic.load

You're trying to copy a non-copyable type: the AtomicUInt64 constructor takes an atomic by value. 您正在尝试复制不可复制的类型: AtomicUInt64构造函数按值获取atomic

If you need it to be initialisable from atomic , then it should take the argument by (const) reference. 如果你需要它可以从atomic初始化,那么它应该采用(const)引用的参数。 However, in your case, it doesn't look like you need to initialise from atomic at all; 但是,在您的情况下,看起来您根本不需要从atomic初始化; why not initialise from uint64_t instead? 为什么不从uint64_t初始化呢?

Also a couple of minor points: 还有几个小问题:

  • The copy constructor and assignment operator should take their values by const reference, to allow temporaries to be copied. 复制构造函数和赋值运算符应该通过const引用获取它们的值,以允许复制临时值。

  • Allocating the vector with new is a rather odd thing to do; new分配矢量是一件相当奇怪的事情; you're just adding an extra level of indirection with no benefit. 你只是增加了额外的间接水平而没有任何好处。

  • Make sure you never resize the array while other threads might be accessing it. 确保在其他线程可能正在访问它时,永远不会调整阵列大小。

This line 这条线

AtomicUInt64 ( std::atomic<uint64_t> a ) : atomic ( atomic.load() ) {}

You're completely ignoring the argument you pass in, You probably want it to be a.load() and you probably want to take elements by const reference so they aren't copied. 您完全忽略了传入的参数,您可能希望它是a.load(),并且您可能希望通过const引用获取元素,这样它们就不会被复制。

AtomicUInt64 (const std::atomic<uint64_t>& a) : atomic (a.load()) {}

As for what you're doing, I'm not sure if it is correct. 至于你在做什么,我不确定它是否正确。 The modification of the variables inside the array will be atomic, but if the vector is modified or reallocated (which is possible with push_back ), then there's nothing to guarantee your array modifications will work between threads and be atomic. 对数组内部变量的修改将是原子的,但是如果向量被修改或重新分配(可以使用push_back ),那么没有什么可以保证你的数组修改在线程之间工作并且是原子的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM