Самореализуемый потокобезопасный строковый буфер в C ++

0

Является ли этот кусок кода рассмотренным как поточно-безопасный? Когда я использую буфер, он будет иногда сбой, и я думаю, что это способствовало возникновению проблем с данными, есть ли проблемы с этой реализацией?

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
  • 1
    Вы, кажется, нигде не устанавливаете для isBusy значение true.
  • 0
    О боже, это глупая ошибка. Но буфер все еще поврежден после добавления isBusy.store(true); прямо перед изменением буфера
Показать ещё 1 комментарий
Теги:
multithreading

2 ответа

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
}
  • 0
    Следующий вопрос: зачем использовать такой класс вместо std::mutex ?
  • 0
    ОП, похоже, заинтересован в вращающемся решении. std::mutex может вращаться, но в конце концов засыпает.
Показать ещё 2 комментария
1

"Есть ли проблемы с этой реализацией?"

Одна проблема, которую я вижу, заключается в том, что std::atomic<bool> isBusy; не заменит std::mutex для блокировки одновременного доступа к m_buffer. Вы никогда не устанавливаете значение в true.

Но даже если вы это сделаете (как видно из вашего редактирования), операции store() и load() для значения isBusy не образуют блокировку для защиты доступа к m_buffer целиком. Переключатели контекста потока могут возникать между ними.

  • 0
    Я пытаюсь использовать isBusy для имитации блокировки. Когда выполняется операция (чтение или запись), она сначала гарантирует, что буфер не занят, а затем выполняет соответствующую операцию. Я новичок в многопоточных программах, поэтому я не уверен, что моя концепция верна.
  • 1
    @Alvin Как отметил Делнан в своем комментарии, вы никогда не устанавливали значение true . Таким образом, он не может обеспечить замену мьютекса.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню