C ++线程安全整数 [英] C++ Thread Safe Integer

查看:117
本文介绍了C ++线程安全整数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前为线程安全整数创建了一个C ++类,它只是私有地存储一个整数,并且有一个public函数,它使用一个boost :: mutex来确保每次只能有一个变化应用于整数。

I have currently created a C++ class for a thread safe integer which simply stores an integer privately and has public get a set functions which use a boost::mutex to ensure that only one change at a time can be applied to the integer.

这是最有效的方法,我已经被告知互斥体是相当资源密集型?该类使用很多,非常迅速,因此很可能是一个瓶颈...

Is this the most efficient way to do it, I have been informed that mutexes are quite resource intensive? The class is used a lot, very rapidly so it could well be a bottleneck...

Googleing C ++ Thread Safe Integer 返回不清楚的视图

Googleing C++ Thread Safe Integer returns unclear views and oppinions on the thread safety of integer operations on different architectures.

有人说,32位的arch上的32位int是安全的,但是32位的32位int不是因为'对齐'其他人说这是编译器/操作系统特定的(我不怀疑)。

Some say that a 32bit int on a 32bit arch is safe, but 64 on 32 isn't due to 'alignment' Others say it is compiler/OS specific (which I don't doubt).

我在32位机器上使用Ubuntu 9.10,在某些情况下可以同时在不同的内核上执行线程,我使用的是GCC 4.4的g ++编译器。

I am using Ubuntu 9.10 on 32 bit machines, some have dual cores and so threads may be executed simultaneously on different cores in some cases and I am using GCC 4.4's g++ compiler.

提前感谢...

请注意: 我标记为正确的答案最适合我的问题 - 但是在其他答案中有一些优点都是值得阅读的!

推荐答案

这不是编译器和操作系统,编译器和操作系统进入它,因为它们是你工作的工具,但它们不是设置真正规则的工具。这就是为什么C ++标准不会碰到这个问题。

It's not compiler and OS specific, it's architecture specific. The compiler and OS come into it because they're the tools you work through, but they're not the ones setting the real rules. This is why the C++ standard won't touch the issue.

我从来没有听说过64位整数写入,可以分为两个32位写入,中途中断。 (是的,这是邀请他人发表反例)具体来说,我从来没有听说过一个CPU的加载/存储单元允许中断不对齐的写;一个中断源必须等待整个未对齐的访问完成。

I have never in my life heard of an 64-bit integer write, which can be split into two 32-bit writes, being interrupted halfway through. (Yes, that's an invitation to others to post counterexamples.) Specifically, I have never heard of a CPU's load/store unit allowing a misaligned write to be interrupted; an interrupting source has to wait for the whole misaligned access to complete.

要有一个可中断的加载/存储单元,其状态必须保存到堆栈中。 。并且加载/存储单元是将CPU的其余状态保存到堆栈中的。如果加载/存储单元是可中断的,那么这将是非常复杂的,并且容易出错的,并且所有你将获得的是在响应中断时一个周期更少的延迟,其最多以数十个循环测量。完全不值得。

To have an interruptible load/store unit, its state would have to be saved to the stack... and the load/store unit is what saves the rest of the CPU's state to the stack. This would be hugely complicated, and bug prone, if the load/store unit were interruptible... and all that you would gain is one cycle less latency in responding to interrupts, which, at best, is measured in tens of cycles. Totally not worth it.

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

Back in 1997, A coworker and I wrote a C++ Queue template which was used in a multiprocessing system. (Each processor had its own OS running, and its own local memory, so these queues were only needed for memory shared between processors.) We worked out a way to make the queue change state with a single integer write, and treated this write as an atomic operation. Also, we required that each end of the queue (i.e. the read or write index) be owned by one and only one processor. Thirteen years later, the code is still running fine, and we even have a version that handles multiple readers.

仍然,如果你想对待一个64位整数写作为原子,将字段对齐到64位绑定。为什么要担心?

Still, if you want to treat a 64-bit integer write as atomic, align the field to a 64-bit bound. Why worry?

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

For the case you mention in your comment, I'd need more information to be sure, so let me give an example of something that could be implemented without specialized synchronization code.

假设你有N个写者和一个读者。你希望作家能够向读者发出事件的信号。事件本身没有数据;

Suppose you have N writers and one reader. You want the writers to be able to signal events to the reader. The events themselves have no data; you just want an event count, really.

声明共享内存的结构,在所有作者和读者之间共享:

Declare a structure for the shared memory, shared between all writers and the reader:

#include <stdint.h>
struct FlagTable
{   uint32_t flag[NWriters];
};

(将此类设为类别或模板或任何您认为合适的内容。)

(Make this a class or template or whatever as you see fit.)

每个作者需要被告知其索引并给出一个指向这个表的指针:

Each writer needs to be told its index and given a pointer to this table:

class Writer
{public:
    Writer(FlagTable* flags_, size_t index_): flags(flags_), index(index_) {}
    void SignalEvent(uint32_t eventCount = 1);
private:
    FlagTable* flags;
    size_t index;
}

当作者想要发出一个事件flag:

When the writer wants to signal an event (or several), it updates its flag:

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;
}



读取器保留其看到的所有标志值的本地副本: / p>

The reader keeps a local copy of all the flag values it has seen:

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];
}

要查明是否发生任何事件,

To find out if any events have happened, it just looks for changed values:

bool Reader::AnyEvents(void)
{   for(size_t i = 0; i < NWriters; ++i)
        if(seenFlags[i] != flags->flag[i])
            return true;
    return false;
}

如果发生了什么事,我们可以检查每个源并获取事件计数: / p>

If something happened, we can check each source and get the event count:

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;
}

它是非阻塞的,这就是说,你不能让读者睡着,直到一个作家写的东西。读取器必须选择坐在自旋循环中等待 AnyEvents()以返回 true ,从而最小化延迟,或者它可以每次睡眠一次,这会节省CPU,但可以让很多事件积累。

Now the big gotcha in all this? It's nonblocking, which is to say that you can't make the Reader sleep until a Writer writes something. The Reader has to choose between sitting in a spin-loop waiting for AnyEvents() to return true, which minimizes latency, or it can sleep a bit each time through, which saves CPU but could let a lot of events build up. So it's better than nothing, but it's not the solution to everything.

使用实际的同步原语,只需要使用互斥体和条件变量来包装这个代码,这样就可以它正确地阻塞:读者会睡觉,直到有事要做。由于您对标志使用了原子操作,因此实际上可以将互斥体锁定的时间量保持为最小值:Writer只需要锁定互斥体足够长的时间来发送条件,而不设置标志,只需要在调用 AnyEvents()之前等待条件(基本上,它就像上面的sleep-loop情况,但是使用wait-for-condition而不是sleep )。

Using actual synchronization primitives, one would only need to wrap this code with a mutex and condition variable to make it properly blocking: the Reader would sleep until there was something to do. Since you used atomic operations with the flags, you could actually keep the amount of time the mutex is locked to a minimum: the Writer would only need to lock the mutex long enough to send the condition, and not set the flag, and the reader only needs to wait for the condition before calling AnyEvents() (basically, it's like the sleep-loop case above, but with a wait-for-condition instead of a sleep call).

这篇关于C ++线程安全整数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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