Чтение и запись с использованием одного и того же критического раздела - PullRequest
4 голосов
/ 09 июня 2011

Мне нужно написать класс, который читает и пишет в файл. Когда я делаю операцию записи, чтение не должно происходить, а также наоборот. Могу ли я использовать один объект критического сечения для этого? Как это:

FileWorker.h

class FileWorker
{
public:
    FileWorker();
    void WriteIntoFile(const char* fileName, const char* stringToWrite);
    void ReadFromFile(const char* fileName, char* stringToRead, int* stringLength);
    ~FileWorker();
};

FileWorker.cpp

#include <windows.h>
#include "FileWorker.h"

static CRITICAL_SECTION g_criticalSection;

FileWorker::FileWorker()
{
#ifdef WIN32APP
    InitializeCriticalSection(&g_criticalSection);
#endif
}

void FileWorker::ReadFromFile(const char *fileName, char *stringToRead, int *stringLength)
{
    EnterCriticalSection(&g_criticalSection);
    // Do Read
    LeaveCriticalSection(&g_criticalSection);
}

void FileWorker::WriteIntoFile(const char *fileName, const char *stringToWrite)
{
    EnterCriticalSection(&g_criticalSection);
    // Do Write
    LeaveCriticalSection(&g_criticalSection);
}

FileWorker::~FileWorker()
{
#ifdef WIN32APP
    DeleteCriticalSection(&g_criticalSection);
#endif
}

Спасибо.

Ответы [ 3 ]

3 голосов
/ 09 июня 2011

Как указывалось в других ответах, один глобальный критический раздел приводит к проблемам при использовании нескольких экземпляров FileWorker одновременно.Вы должны сделать критический раздел членом FileWorker и инициализировать / удалить его в конструкторе / деструкторе.

Для реализации блокировки я бы рекомендовал написать небольшой вспомогательный класс для поддержки блокировки по областям:

class ScopedCriticalSection {
public:
    ScopedCriticalSection(CRITICAL_SECTION & criticalSection)
      : m_criticalSection(criticalSection)
    {
        EnterCriticalSection(&m_criticalSection);
    }

    ~ScopedCriticalSection() {
        LeaveCriticalSection(&m_criticalSection);
    }
private:
    CRITICAL_SECTION & m_criticalSection;
}

Вы можете использовать этот объект следующим образом:

void FileWorker::ReadFromFile(const char *fileName, char *stringToRead, 
                              int *stringLength)
{
    ScopedCriticalSection guard(m_criticalSection); // enters the cs
    // read stuff
} // the critical section is left when the guard is destroyed

Чтобы понять, как это работает, прочитайте о RAII .

3 голосов
/ 09 июня 2011

Если вы говорите об одном и том же файле для чтения / записи, то вам нужно использовать тот же критический раздел (как вы это сделали в настоящее время), иначе один поток мог бы читать файл, а другой поток записывал в него Это именно то, что вы используете, чтобы избежать критических разделов.

Однако, так как ваш класс FileWorker в настоящее время написан, вы можете читать / записывать произвольные файлы, но иметь один глобальный критический раздел. Это все еще работает в этом сценарии, но это приведет к дополнительным накладным расходам, если редко возникает конфликт для одного и того же файла. Это может быть приемлемым компромиссом для вас, в зависимости от ваших моделей использования и того, насколько критичен этот код по времени.

Кроме того, как указал Бегемот, у вас будут проблемы с одним глобальным критическим разделом, если вы создадите два FileWorkers с перекрывающимися временами жизни. Вам понадобится нечто похожее на то, что было предложено, чтобы убедиться, что вы не пытаетесь инициализировать критический раздел, который уже был инициализирован, или удалять уже удаленный.

0 голосов
/ 09 июня 2011

Для защиты общего ресурса вам необходим один и тот же критический раздел всеми потоками.Код в порядке, кроме конструктора и деструктора.Этот код приводит к неопределенному поведению:

FileWorker a;
FileWorker b;

, потому что g_criticalSection инициализируется дважды без промежуточного удаления.Вам нужно добавить статические функции-члены для инициализации и финализации вашего класса.

static void FileWorker::initialize()
{
  InitializeCriticalSection(&g_criticalSection);
}

static void FileWorker::finialize()
{
  DeleteCriticalSection(&g_criticalSection);
}

Когда вам нужно синхронизировать данные для каждого файла, правильный способ - реализовать абстракцию поверх ваших файловых средств ввода-вывода.(РУЧКИ, ФАЙЛ * s, std :: fstream и т. Д.).Например,

class SyncronizedOutStream
{
  CRITICAL_SECTION cs;
  std::ofstream ostm;

  SyncronizedOutStream(const SyncronizedOutStream&);
  void operator= (const SyncronizedOutStream&);
public:
  explicit SyncronizedOutStream(const std::string& filename) 
    : ostm(filename.c_str())
  {
    InitializeCriticalSection(&cs);
  }

  ~SyncronizedOutStream()
  {
    DeleteCriticalSection(&cs);
  }

  template<typename T>
  SyncronizedOutStream& operator<< (T x)
  {
     ostm << x;
     return *this;
  }

  void lock()
  {
    EnterCriticalSection(&cs);
  }

  void release()
  {
    LeaveCriticalSection(&cs);
  }
};

Экземпляры этого класса не подлежат копированию и назначению, это важно, потому что критический раздел должен быть скопирован.

...