使用相同的临界区对象进行读写

时间:2011-06-09 04:49:20

标签: c++ file-io critical-section

我需要编写一个读写文件的类。当我进行写操作时,不应该进行读取,反之亦然。我可以使用单个关键部分对象吗?像这样:

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 个答案:

答案 0 :(得分:3)

如果您正在谈论相同的文件进行读/写,那么您需要使用相同的关键部分(就像您目前所做的那样),否则一个线程可能正在读取该文件,而另一个线程正在写对它来说,这正是你使用关键部分要避免的。

但是,当前编写FileWorker类的方式,您可以读/写任意文件,但只有一个全局关键部分。这仍然适用于这种情况,但如果很少争用同一个文件,它最终会增加额外的开销。根据您的使用模式以及此代码的时间关键程度,这可能是您可以接受的权衡。

此外,正如Begemoth所指出的,如果您创建两个具有重叠生命周期的FileWorkers,那么您将遇到单个全局关键部分的问题。您需要与提议的内容类似的内容,以确保您不会尝试初始化已经初始化的关键部分,或者删除已经删除的部分。

答案 1 :(得分:3)

正如其他答案所指出的,当同时使用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

答案 2 :(得分:0)

所有线程都需要相同的关键部分来保护共享资源。除了构造函数和析构函数之外,代码是可以的。此代码导致未定义的行为:

FileWorker a;
FileWorker b;

因为g_criticalSection被初始化两次而没有中间删除。您需要添加静态成员函数来初始化和完成您的类。

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

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

当你需要在每个文件的基础上进行同步时,正确的方法是在你的文件I / O手段(HANDLEs,FILE * s,std :: fstream等)之上实现抽象。 E.g。

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

此类的实例不可复制且不可分配这很重要,因为必须复制关键部分。