[英]Self-implemented thread-safe string buffer in C++
Is this piece of code considered as thread-safe? 这段代码是否被视为线程安全的? When I consume the buffer, it will crash sometimes and i think it is contributed to data-racing problems, is there any problem with this implementation? 当我使用该缓冲区时,它有时会崩溃,并且我认为这是导致数据竞赛问题的原因,此实现是否存在任何问题?
TSByteBuf.cpp TSByteBuf.cpp
#include "TSByteBuf.h"
int TSByteBuf::Read(byte* buf, int len)
{
while (true)
{
if (isBusy.load())
{
//Sleep(10);
}else
{
isBusy.store(true);
int dByteGet = m_buffer.sgetn((char*) buf, len);
isBusy.store(false);
return dByteGet;
}
}
}
int TSByteBuf::Write(byte* buf, int len)
{
while (true)
{
if (isBusy.load())
{
//Sleep(10);
}else
{
isBusy.store(true);
int dBytePut = m_buffer.sputn((char*) buf, len);
isBusy.store(false);
return dBytePut;
}
}
}
TSByteBuf.h TSByteBuf.h
#ifndef TSBYTEBUF_H
#define TSBYTEBUF_H
#include <sstream>
#include <atomic>
typedef unsigned char byte;
class TSByteBuf
{
public:
std::stringbuf m_buffer;
//bool Write(byte* buf, int len);
//bool Read(byte* buf, int len);
int Write(byte* buf, int len);
int Read(byte* buf, int len);
protected:
std::atomic<bool> isBusy;
};
#endif
There's a race between the threads trying to set the isBusy
variable. 尝试设置isBusy
变量的线程之间存在竞争。 With std::atomic<>
, loads and stores are guaranteed to be atomic, but there's a time windows between those two operations in the code. 使用std::atomic<>
,可以保证加载和存储是原子的,但是在代码中的这两个操作之间有一个时间窗口。 You need to use a different set of functions that provide the two atomically. 您需要使用一组不同的函数来自动提供这两个功能。 See compare_exchange . 参见compare_exchange 。
You can make your life easier by using the tools offered by the C++ standard library. 您可以使用C ++标准库提供的工具使您的生活更轻松。 To make sure only one thread accesses the given area (has an exclusive access) at a time, you can use std::mutex
. 要确保一次只有一个线程访问给定区域(具有独占访问权),可以使用std::mutex
。 Further you can use std::lock_guard
, which will automatically lock (and unlock with the end of the scope) the mutex for you. 此外,您可以使用std::lock_guard
,它将自动为您锁定(并在作用域的末尾解锁)互斥锁。
int TSByteBuf::Read(byte* buf, int len)
{
std::lock_guard<std::mutex> lg(mutex);
// do your thing, no need to unlock afterwards, the guard will take care of it for you
}
The mutex
variable needs to be shared between the threads, make it a member variable of the class. mutex
变量需要在线程之间共享,使其成为该类的成员变量。
There's an alternative to using std::mutex
by creating your own locking mechanism if you want to make sure the thread never goes to sleep. 如果要确保线程永远不会进入睡眠状态,则可以通过创建自己的锁定机制来替代使用std::mutex
。 As pointed out in the comments, you probably don't need this and the usage of std::mutex
will be fine. 正如评论中指出的那样,您可能不需要它,并且使用std::mutex
会很好。 I'm keeping it here just for a reference. 我将其保留在这里仅供参考。
class spin_lock {
public:
spin_lock() : flag(ATOMIC_FLAG_INIT) {}
void lock() {
while (flag.test_and_set(std::memory_order_acquire))
;
}
void unlock() { flag.clear(std::memory_order_release); }
private:
std::atomic_flag flag;
};
Notice the use of the more lightweight std::atomic_flag
. 注意使用更轻量级的std::atomic_flag
。 Now you can use the class like this: 现在您可以使用这样的类:
int TSByteBuf::Read(byte* buf, int len)
{
std::unique_lock<spin_lock> lg(spinner);
// do your thing, no need to unlock afterwards, the guard will take care of it for you
}
"is there any problem with this implementation?" “此实现有任何问题吗?”
One problem I spot, is that std::atomic<bool> isBusy;
我发现的一个问题是std::atomic<bool> isBusy;
wouldn't replace a std::mutex
for locking concurrent access to m_buffer
. 不会替换std::mutex
来锁定对m_buffer
并发访问。 You never set the value to true
. 您永远不会将值设置为true
。
But even if you do so (as seen from your edit), store()
and load()
operations for the isBusy
value don't form a lock to protect access to m_buffer
in whole. 但是,即使您这样做(从编辑中可以看到), isBusy
值的store()
和load()
操作也不会形成锁来保护整个m_buffer
访问。 Thread context switches may occur in between. 线程上下文切换可能在两者之间发生。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.