可以使用原子来减少在读取的主导多线程程序中的锁定吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可以使用原子来减少在读取的主导多线程程序中的锁定吗?相关的知识,希望对你有一定的参考价值。

最近,我发现自己经常处于共享数据被读取很多但很少被写入的情况,所以我开始怀疑是否可以稍微加快同步速度。

以下面的示例为例,其中多个线程偶尔写入数据,单个线程经常读取数据,所有线程都与普通互斥锁同步。

#include <iostream>
#include <unistd.h>
#include <unordered_map>
#include <mutex>
#include <thread>

using namespace std;

unordered_map<int, int> someData({{1,10}});
mutex mu;

void writeData(){
    while(true){
        {
            lock_guard<mutex> lock(mu);
            int r = rand()%10;
            someData[1] = r;
            printf("data changed to %d
", r);
        }
        usleep(rand()%100000000 + 100000000);
    }
}

void readData(){
    while(true){
        {
            lock_guard<mutex> lock(mu);
            for(auto &i:someData){
                printf("%d:%d
", i.first, i.second);
            }
        }
        usleep(100);
    }

}

int main() {
    thread writeT1(&writeData2);
    thread writeT2(&writeData2);
    thread readT(&readData2);
    readT.join();
}

使用普通的锁定机制,每次读取都需要一个锁定,我正在考虑加快单个原子读取的速度。[[在大多数情况下]:]]unordered_map<int, int> someData({{1,10}}); mutex mu; atomic_int dataVersion{0}; void writeData2(){ while(true){ { lock_guard<mutex> lock(mu); dataVersion.fetch_add(1, memory_order_acquire); int r = rand()%10; someData[1] = r; printf("data changed to %d ", r); } usleep(rand()%100000000 + 100000000); } } void readData2(){ mu.lock(); int versionCopy = dataVersion.load(); auto dataCopy = someData; mu.unlock(); while(true){ if (versionCopy != dataVersion.load(memory_order_relaxed)){ lock_guard<mutex> lock(mu); versionCopy = dataVersion.load(memory_order_relaxed); dataCopy = someData; } else{ for(auto &i:dataCopy){ printf("%d:%d ", i.first, i.second); } usleep(100); } } }

这里的数据类型unordered_map只是一个例子,它可以是任何类型,并且我不是在寻找纯粹的无锁算法,因为这可能是另外一回事了。仅对于基于普通锁的同步,在大多数操作被读取的情况下,使用这样的技巧,

从逻辑上讲可以吗?有没有确定的方法?

[编辑]

    我知道
  • 共享的互斥锁
,但这并不是我所说的情况。首先,共享锁并不便宜,可能比普通的互斥锁更昂贵,当然也比原子重。其次,在示例中,我展示了一个无法充分利用它的读取线程。我对锁定操作成本特别感兴趣。减少阻塞,确保关键部分确实是在实际情况下首先要研究的内容,但我在这里没有针对性。
  • [unordered_map数据类型只是一个例子
  • ,而不是寻找更适合特定任务的数据结构或无锁定算法,该数据类型可以是任何东西。睡眠时间是要证明
  • 读取发生的次数远大于写入发生的次数>>,在某种程度上我们开始不太在意if块中的额外锁定和复制时间。
  • 谢谢〜

    最近,我发现自己经常处于共享数据被读取很多而却很少被写入的情况,因此我开始怀疑是否可以稍微加快同步速度。将以下内容作为...

    您正在将数据存储在unordered_map中。 unordered_map类可以保证读写器的并发访问。如果对该前景不满意,那么原子不是您的朋友。

    [在大多数(每个?)操作系统中,在没有争议的情况下,锁定原语本身是由原子处理的;仅在竞争时恢复到内核。考虑到这一点,最好在保持锁的同时尽量减少代码量,因此第一个循环应为:

    int r = rand()%10; mu.lock(); someData[1] = r; mu.unlock(); printf("data changed to %d ", r);

    我不知道您将如何解决读取方面的问题,但是如果您选择一个友好的数据存储,则可以用相同的方式将对它的访问减至最少。

    我将首先尝试描述我对您的想法的理解:

      频繁读取,偶尔写入。
  • 锁是昂贵的...应该进行基准测试,尝试std::shared_mutexstd::shared_mutex,或其他一些
  • slim
  • 实现,它们通常使用一些便宜而乐观的(原子/自旋锁)机制,几乎没有-在没有碰撞的情况下不会产生影响(大多数时候没有作者)。您似乎并不在乎您的副本有多大/最近。对于某些信息性能计数器来说,这是可以接受的,但我会三思而后行-并非其他人必须维护您的代码,这是期望或什至不会想到的事情。其后果可能是灾难性的。
  • 您仅访问锁下的可写数据,请读取您持有锁创建的副本。这意味着从简单的线程同步视图来看,您的方法是[[safe
  • ,除了上述几点(读者使用旧数据,多个读者可以拥有不同的副本...值得吗?)。]
  • 无论如何,您甚至应该尝试提出自己的同步机制之前(通常很难做到),您实际上应该首先尝试进行基准测试,然后再尝试找到其他人已经写过的更好的解决方案(纤细的rw-locks)。正确)。
  • 答案
    您正在将数据存储在unordered_map中。 unordered_map类可以保证读写器的并发访问。如果对该前景不满意,那么原子不是您的朋友。
    另一答案
    我将首先尝试描述我对您的想法的理解:

      频繁读取,偶尔写入。

    以上是关于可以使用原子来减少在读取的主导多线程程序中的锁定吗?的主要内容,如果未能解决你的问题,请参考以下文章

    多线程程序,当一条原子指令刚执行一半的时候,CPU时间片耗尽,此时是立刻切换线程吗?

    C# 当我只读取而不更改队列时,我应该使用锁定语句吗?

    C# - 锁定和 StreamWrite 问题(多线程)

    线程 - AtomicInteger

    多线程应用程序中的非解剖分配

    mysql支持多用户同时读写吗