4

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

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
}

谢谢。

4

3 回答 3

3

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

但是,按照当前编写 FileWorker 类的方式,您可以读取/写入任意文件,但只有一个全局临界区。这在这种情况下仍然有效,但如果很少有同一个文件的争用,它最终会增加额外的开销。这可能是您可以接受的折衷方案,具体取决于您的使用模式以及此代码的时间紧迫性。

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

于 2011-06-09T05:01:29.023 回答
2

正如其他答案所指出的那样,单个全局临界区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

于 2011-06-09T07:12:15.563 回答
0

您需要所有线程都使用相同的临界区来保护共享资源。除了构造函数和析构函数之外,代码都可以。此代码导致未定义的行为:

FileWorker a;
FileWorker b;

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

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

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

当您需要基于每个文件进行同步时,正确的方法是在文件 I/O 方法(HANDLE、FILE*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);
  }
};

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

于 2011-06-09T05:04:08.883 回答