Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/129.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++;线程安全整数 我为一个线程安全的整数创建了一个C++类,它简单地存储一个整数,并且公共地得到一个使用Booo::互斥体的集合函数,以确保每次只能对一个变量进行整型。_C++_Multithreading_Integer_Thread Safety - Fatal编程技术网

C++;线程安全整数 我为一个线程安全的整数创建了一个C++类,它简单地存储一个整数,并且公共地得到一个使用Booo::互斥体的集合函数,以确保每次只能对一个变量进行整型。

C++;线程安全整数 我为一个线程安全的整数创建了一个C++类,它简单地存储一个整数,并且公共地得到一个使用Booo::互斥体的集合函数,以确保每次只能对一个变量进行整型。,c++,multithreading,integer,thread-safety,C++,Multithreading,Integer,Thread Safety,这是最有效的方法吗?我被告知互斥体是资源密集型的?该类被大量使用,速度非常快,因此很可能成为瓶颈 GooGe+C++线程安全整数返回不同架构上整数操作线程安全性的不清晰视图和问题。 有人说32位arch上的32位int是安全的,但32位上的64位并不是因为“对齐”,也有人说它是特定于编译器/操作系统的(我不怀疑这一点) 我在32位机器上使用Ubuntu9.10,有些机器有双核,因此在某些情况下线程可以在不同的核上同时执行,我使用的是GCC4.4的g++编译器 先谢谢你 请注意:我标记为“正确”

这是最有效的方法吗?我被告知互斥体是资源密集型的?该类被大量使用,速度非常快,因此很可能成为瓶颈

GooGe+C++线程安全整数返回不同架构上整数操作线程安全性的不清晰视图和问题。

有人说32位arch上的32位int是安全的,但32位上的64位并不是因为“对齐”,也有人说它是特定于编译器/操作系统的(我不怀疑这一点)

我在32位机器上使用Ubuntu9.10,有些机器有双核,因此在某些情况下线程可以在不同的核上同时执行,我使用的是GCC4.4的g++编译器

先谢谢你


请注意:我标记为“正确”的答案最适合我的问题-但是其他答案中有一些非常好的观点,它们都值得一读

C++没有真正的原子整数实现,大多数常见的库也没有


考虑这样一个事实,即即使存在上述实现,它也必须依赖某种互斥,因为您无法保证跨所有体系结构的原子操作。

有C++0x原子库,还有一个Boost.Atomic库正在开发中,它使用无锁技术。

当您使用GCC时,根据您希望对整数执行的操作,您可能会侥幸逃脱


这些操作可能比互斥体快一点,但在某些情况下仍然比“正常”操作慢得多。

它不是特定于编译器和操作系统的,而是特定于体系结构的。编译器和操作系统进入其中是因为它们是您使用的工具,但它们不是设置真正规则的工具。这就是为什么C++标准不会触及这个问题的原因。 我一生中从未听说过64位整数写入,它可以分为两个32位写入,中途被中断。(是的,这是邀请其他人发表反例。)具体地说,我从未听说过CPU的加载/存储单元允许未对齐的写入被中断;中断源必须等待整个未对齐的访问完成

要拥有可中断的加载/存储单元,必须将其状态保存到堆栈中。。。加载/存储单元将CPU的其余状态保存到堆栈中。如果加载/存储单元是可中断的,那么这将非常复杂,并且容易出现错误。。。你所能得到的就是响应中断的延迟减少一个周期,这最多可以用几十个周期来衡量。完全不值得

回到1997,一个同事和我写了一个C++队列模板,它被用于一个多处理系统。(每个处理器都有自己的操作系统在运行,有自己的本地内存,所以这些队列只需要在处理器之间共享内存。)我们找到了一种方法,通过一个整数写入来改变队列状态,并将此写入视为一个原子操作。此外,我们还要求队列的每一端(即读或写索引)由一个且仅由一个处理器拥有。13年后,代码仍然运行良好,我们甚至有了一个可以处理多个阅读器的版本

不过,如果要将64位整数写入视为原子写入,请将字段与64位绑定对齐。为什么担心

编辑:对于您在评论中提到的情况,我需要更多的信息来确定,所以让我举一个没有专门的同步代码就可以实现的例子

假设你有N个作者和一个读者。您希望编写器能够向读者发送事件信号。事件本身没有数据;你只需要一个事件计数,真的

声明共享内存的结构,在所有写入程序和读取器之间共享:

#include <stdint.h>
struct FlagTable
{   uint32_t flag[NWriters];
};
当写入程序想要发出一个(或多个)事件的信号时,它会更新其标志:

void Writer::SignalEvent(uint32_t eventCount)
{   // Effectively atomic: only one writer modifies this value, and
    // the state changes when the incremented value is written out.
    flags->flag[index] += eventCount;
}
读取器保留其看到的所有标志值的本地副本:

class Reader
{public:
    Reader(FlagTable* flags_): flags(flags_)
    {   for(size_t i = 0; i < NWriters; ++i)
            seenFlags[i] = flags->flag[i];
    }
    bool AnyEvents(void);
    uint32_t CountEvents(int writerIndex);
private:
    FlagTable* flags;
    uint32_t seenFlags[NWriters];
}
现在这一切都被大人物抓住了?它是非阻塞的,也就是说,在作者写东西之前,你不能让读者睡觉。读卡器必须选择坐在旋转循环中等待
AnyEvents()
返回
true
,这样可以最大限度地减少延迟,或者每次都可以休眠一点,这样可以节省CPU,但会让大量事件累积。所以这总比什么都没有好,但这不是解决所有问题的办法


使用实际的同步原语,只需使用互斥体和条件变量包装此代码即可使其正确阻塞:读取器将一直休眠,直到有事情要做。由于您对标志使用了原子操作,因此实际上可以将互斥锁的锁定时间限制在最低限度:编写器只需将互斥锁锁定足够长的时间即可发送条件,而不需要设置标志,而读取器只需等待条件,然后再调用
AnyEvents()
(基本上,它类似于上面的sleep循环情况,但使用等待条件而不是sleep调用).

对于完整的通用同步,正如其他人已经提到的,传统的同步工具是非常必要的。但是,对于某些特殊情况,可以利用硬件优化。特别是,大多数现代CPU都支持整数的原子递增和递减。该库具有相当大的灵活性很好的跨平台支持。基本上,该库为这些操作包装了CPU和编译器特定的汇编代码,并在不可用的情况下默认为互斥保护。它当然不是非常通用,但如果您只对维护计数感兴趣
class Reader
{public:
    Reader(FlagTable* flags_): flags(flags_)
    {   for(size_t i = 0; i < NWriters; ++i)
            seenFlags[i] = flags->flag[i];
    }
    bool AnyEvents(void);
    uint32_t CountEvents(int writerIndex);
private:
    FlagTable* flags;
    uint32_t seenFlags[NWriters];
}
bool Reader::AnyEvents(void)
{   for(size_t i = 0; i < NWriters; ++i)
        if(seenFlags[i] != flags->flag[i])
            return true;
    return false;
}
uint32_t Reader::CountEvents(int writerIndex)
{   // Only read a flag once per function call.  If you read it twice,
    // it may change between reads and then funny stuff happens.
    uint32_t newFlag = flags->flag[i];
    // Our local copy, though, we can mess with all we want since there
    // is only one reader.
    uint32_t oldFlag = seenFlags[i];
    // Next line atomically changes Reader state, marking the events as counted.
    seenFlags[i] = newFlag;
    return newFlag - oldFlag;
}