简体   繁体   English

C ++中的自实现线程安全字符串缓冲区

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM