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

时间:2014-09-21 17:24:38

标签: c++ multithreading

这段代码是否被认为是线程安全的? 当我使用缓冲区时,它有时会崩溃,我认为它会导致数据竞争问题,这个实现有什么问题吗?

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

#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

2 个答案:

答案 0 :(得分:2)

尝试设置isBusy变量的线程之间存在竞争。使用std::atomic<>,加载和存储保证是原子的,但在代码中的这两个操作之间有一个时间窗口。您需要使用一组不同的函数,这些函数以原子方式提供两个函数。请参阅compare_exchange

使用C ++标准库提供的工具,您可以让您的生活更轻松。要确保一次只有一个线程访问给定区域(具有独占访问权限),您可以使用std::mutex。此外,您可以使用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
}

需要在线程之间共享mutex变量,使其成为类的成员变量。

如果要确保线程永远不会进入休眠状态,可以通过创建自己的锁定机制来使用std::mutex。正如评论中指出的那样,您可能不需要这样做,std::mutex的用法就可以了。我把它留在这里只是为了参考。

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

请注意使用更轻量级的std::atomic_flag。现在你可以使用这样的类:

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
}

答案 1 :(得分:1)

  

&#34;此实施有任何问题吗?&#34;

我发现的一个问题是,std::atomic<bool> isBusy;无法替换std::mutex来锁定m_buffer的并发访问权限。您从未将值设置为true

但即使您这样做(从编辑中看到),store()值的load()isBusy操作也不会形成锁定以保护对{的访问权限{1}}整体而言。线程上下文切换可能发生在两者之间。