std::atomic 类型允许对变量进行原子访问,但我有时会喜欢非原子访问,例如当访问受互斥体保护时。考虑一个允许多线程访问(通过插入)和单线程矢量化访问(通过运算符|=)的位域类:
class Bitfield
{
const size_t size_, word_count_;
std::atomic<size_t> * words_;
std::mutex mutex_;
public:
Bitfield (size_t size) :
size_(size),
word_count_((size + 8 * sizeof(size_t) - 1) / (8 * sizeof(size_t)))
{
// make sure words are 32-byte aligned
posix_memalign(&words_, 32, word_count_ * sizeof(size_t));
for (int i = 0; i < word_count_; ++i) {
new(words_ + i) std::atomic<size_t>(0);
}
}
~Bitfield () { free(words_); }
private:
void insert_one (size_t pos)
{
size_t mask = size_t(1) << (pos % (8 * sizeof(size_t)));
std::atomic<size_t> * word = words_ + pos / (8 * sizeof(size_t));
word->fetch_or(mask, std::memory_order_relaxed);
}
public:
void insert (const std::set<size_t> & items)
{
std::lock_guard<std::mutex> lock(mutex_);
// do some sort of muti-threaded insert, with TBB or #pragma omp
parallel_foreach(items.begin(), items.end(), insert_one);
}
void operator |= (const Bitfield & other)
{
assert(other.size_ == size_);
std::unique_lock<std::mutex> lock1(mutex_, defer_lock);
std::unique_lock<std::mutex> lock2(other.mutex_, defer_lock);
std::lock(lock1, lock2); // edited to lock other_.mutex_ as well
// allow gcc to autovectorize (256 bits at once with AVX)
static_assert(sizeof(size_t) == sizeof(std::atomic<size_t>), "fail");
size_t * __restrict__ words = reinterpret_cast<size_t *>(words_);
const size_t * __restrict__ other_words
= reinterpret_cast<const size_t *>(other.words_);
for (size_t i = 0, end = word_count_; i < end; ++i) {
words[i] |= other_words[i];
}
}
};
注意 operator|= 非常接近我的真实代码中的内容,但 insert(std::set) 只是试图捕捉人们可以
acquire lock;
make many atomic accesses in parallel;
release lock;
我的问题是:混合这种原子和非原子访问的最佳方式是什么?下面对 [1,2] 的回答表明强制转换是错误的(我同意)。但该标准肯定允许如此明显的安全访问吗?
更一般地说,是否可以使用读写锁并允许“读者”以原子方式读写,而唯一的“作者”可以非原子方式读写?