什么是“释放序列"?意思是? [英] What does "release sequence" mean?

查看:168
本文介绍了什么是“释放序列"?意思是?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不明白,如果在下面的示例中有2个线程,为什么没有release sequence也会出现问题.我们对原子变量count仅有2个操作.如输出中所示,count随后递减.

I don't understand, why will be problems without release sequence, if we have 2 threads in the example below. We have only 2 operations on the atomic variable count. count is decremented sequently as shown in the output.

来自 Antony Williams C ++并发操作:

我提到即使在store之间有一系列read-modify-write操作序列,也可以在另一个线程的store和一个原子变量的load之间获得一个synchronizes-with relationship.和load,前提是所有操作均已适当标记.如果商店用memory_order_releasememory_order_acq_relmemory_order_seq_cst标记,并且加载用memory_order_consumememory_order_acquirememory_order_seq_cst标记,并且链中的每个操作都加载由之前的操作,则操作链构成 发布顺序 ,初始存储区为synchronizes-with(用于memory_order_acquirememory_order_seq_cst)或为dependency-ordered-before(用于memory_order_consume)的最终负载.链中的任何原子读取-修改-写入操作都可以具有任何内存顺序(甚至是memory_order_relaxed).

I mentioned that you could get a synchronizes-with relationship between a store to an atomic variable and a load of that atomic variable from another thread, even when there’s a sequence of read-modify-write operations between the store and the load, provided all the operations are suitably tagged. If the store is tagged with memory_order_release, memory_order_acq_rel, or memory_order_seq_cst, and the load is tagged with memory_order_consume, memory_order_acquire, or memory_order_seq_cst, and each operation in the chain loads the value written by the previous operation, then the chain of operations constitutes a release sequence and the initial store synchronizes-with (for memory_order_acquire or memory_order_seq_cst) or is dependency-ordered-before (for memory_order_consume) the final load. Any atomic read-modify-write operations in the chain can have any memory ordering (even memory_order_relaxed).

要了解这意味着什么(发布顺序)及其重要性,请考虑使用atomic<int>作为共享队列中项目数的计数,如下所示.

To see what this means (release sequence) and why it’s important, consider an atomic<int> being used as a count of the number of items in a shared queue, as in the following listing.

一种处理方法是让产生数据的线程将这些项目存储在共享缓冲区中,然后执行count.store(number_of_items, memory_order_release) #1 让其他线程知道数据可用.然后,在实际读取共享缓冲区#4 之前,消耗队列项的线程可能会执行count.fetch_sub(1,memory_ order_acquire) #2 从队列中声明一个项.一旦计数变为零,就没有更多的项目了,线程必须等待#3 .

One way to handle things would be to have the thread that’s producingthe data store the items in a shared buffer and then do count.store(number_of_items, memory_order_release) #1 to let the other threads know that data is available. The threads consuming the queue items might then do count.fetch_sub(1,memory_ order_acquire) #2 to claim an item from the queue, prior to actually reading the shared buffer #4. Once the count becomes zero, there are no more items, and the thread must wait #3.

#include <atomic>
#include <thread>
#include <vector>
#include <iostream>
#include <mutex>

std::vector<int> queue_data;
std::atomic<int> count;
std::mutex m;
void process(int i)
{

    std::lock_guard<std::mutex> lock(m);
    std::cout << "id " << std::this_thread::get_id() << ": " << i << std::endl;
}


void populate_queue()
{
    unsigned const number_of_items = 20;
    queue_data.clear();
    for (unsigned i = 0;i<number_of_items;++i)
    {
        queue_data.push_back(i);
    }

    count.store(number_of_items, std::memory_order_release); //#1 The initial store
}

void consume_queue_items()
{
    while (true)
    {
        int item_index;
        if ((item_index = count.fetch_sub(1, std::memory_order_acquire)) <= 0) //#2 An RMW operation
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(500)); //#3
            continue;
        }
        process(queue_data[item_index - 1]); //#4 Reading queue_data is safe
    }
}

int main()
{
    std::thread a(populate_queue);
    std::thread b(consume_queue_items);
    std::thread c(consume_queue_items);
    a.join();
    b.join();
    c.join();
}

输出(VS2015):

id 6836: 19
id 6836: 18
id 6836: 17
id 6836: 16
id 6836: 14
id 6836: 13
id 6836: 12
id 6836: 11
id 6836: 10
id 6836: 9
id 6836: 8
id 13740: 15
id 13740: 6
id 13740: 5
id 13740: 4
id 13740: 3
id 13740: 2
id 13740: 1
id 13740: 0
id 6836: 7

如果有个使用者线程,就可以了; fetch_sub()是具有memory_order_acquire语义的读取,并且存储具有memory_order_release语义,因此存储与负载同步,并且线程可以从缓冲区读取项目.

If there’s one consumer thread, this is fine; the fetch_sub() is a read, with memory_order_acquire semantics, and the store had memory_order_release semantics, so the store synchronizes-with the load and the thread can read the item from the buffer.

如果读取的是两个线程,则第二个fetch_sub()将看到第一个写入的值,而不是商店写入的值.如果没有关于release sequence的规则,则第二个线程与第一个线程将不具有happens-before relationship,并且除非第一个fetch_sub()也具有memory_order_release语义,否则读取共享缓冲区将是不安全的,这将在两个使用者线程之间引入不必要的同步.没有release sequence规则或fetch_sub操作上的memory_order_release,将没有任何要求第二消费者可以看到queue_data的存储,并且您将进行数据争夺.

If there are two threads reading, the second fetch_sub() will see the value written by the first and not the value written by the store. Without the rule about the release sequence, this second thread wouldn’t have a happens-before relationship with the first thread, and it wouldn’t be safe to read the shared buffer unless the first fetch_sub() also had memory_order_release semantics, which would introduce unnecessary synchronization between the two consumer threads. Without the release sequence rule or memory_order_release on the fetch_sub operations, there would be nothing to require that the stores to the queue_data were visible to the second consumer, and you would have a data race.

他是什么意思?那两个线程应该看到count的值是20吗?但是在我的输出中,count随后在线程中递减.

What does he mean? That both threads should see the value of count is 20? But in my output count is sequently decremented in threads.

幸运的是,第一个fetch_sub()确实参与了释放序列,因此store()与第二个fetch_sub()同步.两个使用者线程之间仍然没有同步关系.如图5.7所示.图5.7中的虚线表示释放顺序,实线表示happens-before relationships

Thankfully, the first fetch_sub() does participate in the release sequence, and so the store() synchronizes-with the second fetch_sub(). There’s still no synchronizes-with relationship between the two consumer threads. This is shown in figure 5.7. The dotted lines in figure 5.7 show the release sequence, and the solid lines show the happens-before relationships

推荐答案

他是什么意思?两个线程都应该看到count的值是 20吗但是在我的输出中,线程数随之减少.

What does he mean? That both threads should see the value of count is 20? But in my output count is sequently decremented in threads.

不,他没有.对count的所有修改都是原子的,因此两个阅读器线程在给定的代码中始终会看到不同的值.

No he doesn't. All modification to count are atomic, so both reader threads would always see different values for it in the given code.

他在谈论释放顺序规则的含义,即当给定线程执行release存储时,其他 multiple 线程随后执行相同位置的acquire加载, 释放顺序,其中每个随后的acquire加载都与存储线程具有 happens-before 关系(即,存储区 happens-before 负载).这意味着读取器线程中的加载操作是与写入器线程的同步点,并且存储之前写入器中的所有内存操作必须完成,并且在读取器线程中相应的加载完成后在读取器中是可见的.

He's talking about the implications of the release sequence rule, namely that when a given thread performs a release store, other multiple threads that then perform acquire loads of the same location form a release sequence, in which each subsequent acquire load has a happens-before relationship with the storing thread (i.e. the completion of the store happens-before the load). This means that the load operation in the reader thread is a synchronisation point with the writer thread, and all memory operations in the writer prior to the store must complete and be visible in the reader when its corresponding load completes.

他说的是没有这个规则,因此只有第一个线程会同步到编写器.因此,第二个线程将在访问queue时发生数据争用(请注意:不是 count,无论如何,它都受到原子访问的保护).从理论上讲,只有在其对count的自身加载操作之后,才能通过编号为2的读取器线程看到对countstore之前发生的数据的存储操作.释放顺序规则确保不会发生这种情况.

He's saying that without this rule, only the first thread would be thus synchronised to the writer. The second thread would therefore have a data race in accessing queue (note: not count, which is protected anyway by atomic access). Theoretically, memory operations on data occurring before the store on count could be seen by reader thread number 2 only after its own load operation on count. The release sequence rule assures that this will not happen.

总结:发行顺序规则确保多个线程可以在单个存储上同步其负载.有问题的同步是内存对数据的访问,而不是同步实际原子变量的数据(由于原子,该变量无论如何都会保证同步).

In summary: the release sequence rules assures multiple threads can synchronise their loads on a single store. The synchronisation in question is that of memory accesses to data other than the actual atomic variable being synchronised on (which is guaranteed to be synchronised anyway due to being atomic).

在此处添加注释:在大多数情况下,此类问题仅与那些对重新排序其内存操作放宽的CPU体系结构有关.英特尔架构不是其中之一:它是 strong-ordered ,并且只有少数几种非常特殊的情况可以对内存操作进行重新排序.这些细微差别仅在谈论其他体系结构(例如ARM和PowerPC)时才有意义.

Note to add here: for the most part these kind of issues are only of concern on CPU architectures that are relaxed about reordering their memory operations. The Intel architecture is not one of them: it is strongly-ordered and has only a few very specific circumstances in which memory operations can ever be reordered. These kind of nuances are mostly only relevant when talking about other architectures, such as ARM and PowerPC.

这篇关于什么是“释放序列"?意思是?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆