8

I am implementing some file system in C++. Up to now I was using fstream but I realized that it is impossible to open it in exclusive mode. Since there are many threads I want to allow multiple reads, and when opening file in writing mode I want to open the file in exclusive mode?
What is the best way to do it? I think Boost offers some features. And is there any other possibility? I would also like to see simple example. If it is not easy / good to do in C++ I could write in C as well.

I am using Windows.

4

5 回答 5

4

在许多操作系统上,这根本不可能,因此 C++ 不支持它。您必须编写自己的streambuf. 如果您担心的唯一平台是 Windows,您可以使用它提供的独占模式打开。然而,更有可能的是,您想要使用某种文件锁定,这种锁定更精确,并且在大多数(如果不是所有)平台上都可用(但不是可移植的——LockFileEx 在 Windows 下,fcntl在 Unix 下需要)。

在 Posix 下,您还可以使用pthread_rwlock. Butenhof 使用 C++11 中存在的经典互斥锁和条件变量提供了一个实现,因此您实际上可以实现一个可移植版本(假设所有读取器和写入器都在同一个进程中 - Posix 请求将跨进程边界,但这不适用于 C++ 线程原语)。

于 2013-04-22T08:20:41.673 回答
3

如果您的应用程序仅适用于 Windows,则您可以选择 win32 API 函数“ CreateFile() ”。

例如: HANDLE hFile = ::CreateFileW(lpszFileFullPathName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);

于 2013-04-22T13:15:05.510 回答
2

如果您愿意使用 boost,那么我建议您使用 file_lock 类。这意味着您要保留打开/关闭文件的文件名,因为 fstream 不会为您这样做。

它们有两种lock()可用于写入的模式(即一次只有一个这样的锁,共享锁也可以防止这种锁)和lock_sharable()可用于读取的模式(即任何数量的线程都可以获得这样的锁)。

请注意,您最终会发现以这种方式管理读取和写入都变得复杂。也就是说,如果总是有人阅读,共享锁可能永远不会被释放。在那种情况下,排他锁永远不会有机会获得......

// add the lock in your class
#include <boost/interprocess/sync/file_lock.hpp>
class my_files
{
...
private:
    ...
    boost::file_lock     m_lock;
};

现在,当您要访问文件时,可以以一种或另一种方式锁定它。如果线程负责他们何时执行此操作,您可以添加功能以供用户访问锁。如果您在 my_files 中实现读取和写入功能,您希望获得一个基于堆栈的对象,该对象为您锁定和解锁 (RAII):

class safe_exclusive_lock
{
public:
    safe_exclusive_lock(file_lock & lock)
        : m_lock_ref(lock)
    {
        m_lock_ref.lock();
    }
    ~safe_exclusive_lock()
    {
        m_lock_ref.unlock();
    }
private:
    file_lock & m_lock_ref;
};

现在您可以安全地锁定文件(即您锁定,做一些可能抛出的事情,您总是在退出当前的 {}-block 之前解锁):

ssize_t my_files::read(char *buf, size_t len)
{
    safe_exclusive_lock guard(m_lock);
    ...your read code here...
    return len;
} // <- here we get the unlock()

ssize_t my_files::write(char const *buf, size_t len)
{
    safe_exclusive_lock guard(m_lock);
    ...your write code here...
    return len;
} // <- here we get the unlock()

file_lock 使用一个文件,因此您希望在创建 file_lock 时已经创建 fstream 文件。如果 fstream 文件可能没有在您的构造函数中创建,您可能需要将 m_lock 变量转换为唯一指针:

private:
    std::unique_ptr<file_lock>  m_lock;

当你引用它时,你现在需要一个星号:

safe_exclusive_lock guard(*m_lock);

请注意,为了安全起见,您应该检查指针是否确实已分配,如果未定义,则表示文件尚未打开,因此我建议您抛出:

if(m_lock)
{
    safe_exclusive_lock guard(*m_lock);
    ...do work here...
}
else
{
    throw file_not_open();
}
// here the lock was released so you cannot touch the file anymore

在公开场合,您创建了锁:

bool open(std::string const & filename)
{
    m_stream.open(...);
    ...make sure it worked...
    m_lock.reset(new file_lock(filename));
    // TODO: you may want a try/catch around the m_lock and
    //       close the m_stream if it fails or use a local
    //       variable and swap() on success...
    return true;
}

并且不要忘记在关闭时释放锁定对象:

void close()
{
    m_lock.reset();
}
于 2016-06-01T03:26:45.600 回答
0

好吧,如果文件已经以写入模式打开,您可以手动阻止自己打开文件。只需在内部跟踪您在写入模式下打开的文件。

也许您可以散列文件名并将其存储在具有写访问权限的表中。这将允许快速查找以查看文件是否已打开。

于 2013-04-22T06:52:11.673 回答
0

您可以重命名文件,以新名称更新它,然后将其重命名。我做了,但是有点重。

于 2016-05-31T19:00:39.763 回答