5

在 C++ 中,有没有办法在以下情况下使写入文件线程安全?

void foo_one(){
lock(mutex1);
//open file abc.txt
//write into file
//close file
unlock(mutex1);
}

void foo_two(){
lock(mutex2);
//open file abc.txt
//write into file
//close file
unlock(mutex2);
}

在我的应用程序(多线程)中,foo_one() 和 foo_two() 很可能同时由两个不同的线程执行。有什么方法可以使上述线程安全吗?

我考虑过使用文件锁( fcntl 和/或 lockf ),但不确定如何使用它们,因为 fopen() 已在应用程序中使用(性能原因),并且在某处声明不应使用这些文件锁使用 fopen (因为它是缓冲的)

PS:函数 foo_one() 和 foo_two() 属于两个不同的类,它们之间没有办法共享数据:( 遗憾的是,设计是一个函数不能调用另一个函数。

4

5 回答 5

8

添加记录功能。
这两个函数都调用日志记录函数(执行适当的锁定)。

mutex  logMutex;
void log(std::string const& msg)
{
    RAIILock  lock(logMutex);

    // open("abc.txt");
    // write msg
    // close
}
于 2012-10-15T05:37:19.530 回答
2

如果你真的需要一个记录器,不要试图简单地通过写入文件来完成它,也许使用一个专用的记录器,从而将关注点与你正在编写的代码分开。有许多线程安全的记录器:首先想到的是:g2log。进一步谷歌搜索你会发现log4cplus ,这里的讨论,甚至是极简主义的讨论,+1

于 2012-10-15T05:36:16.297 回答
1

你应该这样做,有一个带有互斥锁和 ofstream 的结构:

struct parser {
    ofstream myfile
    mutex lock
};

然后您可以将此结构 (a) 作为 void* 传递给 foo1 和 foo2

parser * a = new parser();

初始化互斥锁,然后您可以将结构传递给这两个函数。

void foo_one(void * a){
     parser * b = reinterperet_cast<parser *>(a);
     lock(b->lock);
         b->myfile.open("abc.txt");
         //write into file
         b->myfile.close();
      unlock(b->mutex);
}

您可以对 foo_two 函数执行相同的操作。这将提供一种线程安全的方法来写入同一个文件。

于 2012-10-15T05:33:06.927 回答
1

foo_one()如果函数的本质foo_two()只是打开文件,写一些东西,然后关闭它,那么使用相同的互斥锁来防止它们相互混淆:

void foo_one(){
  lock(foo_mutex);
  //open file abc.txt
  //write into file
  //close file
  unlock(foo_mutex);
}

void foo_two(){
  lock(foo_mutex);
  //open file abc.txt
  //write into file
  //close file
  unlock(foo_mutex);
}

当然,这是假设这些是唯一的作者。如果其他线程或进程写入文件,锁定文件可能是个好主意。

于 2012-10-15T05:24:57.417 回答
0

试试这个代码。我已经用MFC 控制台应用程序完成了这个

#include "stdafx.h"
#include <mutex>

CWinApp theApp;
using namespace std;


const int size_ = 100; //thread array size
std::mutex mymutex;
void printRailLock(int id) {
    printf("#ID :%", id);
    lock_guard<std::mutex> lk(mymutex); // <- this is the lock
    CStdioFile lastLog;
    CString logfiledb{ "_FILE_2.txt" };
    CString str;
    str.Format(L"%d\n", id);
    bool opend = lastLog.Open(logfiledb, CFile::modeCreate | CFile::modeReadWrite | CFile::modeNoTruncate);
    if (opend) {
        lastLog.SeekToEnd();
        lastLog.WriteString(str);
        lastLog.Flush();
        lastLog.Close();
    }
}
int main()
{
    int nRetCode = 0;
    HMODULE hModule = ::GetModuleHandle(nullptr);

    if (hModule != nullptr)
    {       
        if (!AfxWinInit(hModule, nullptr, ::GetCommandLine(), 0))
        {            
            wprintf(L"Fatal Error: MFC initialization failed\n");
            nRetCode = 1;
        }
        else
        {
            std::thread threads[size_];
            for (int i = 0; i < size_; ++i) {
                threads[i] = std::thread(printRailLock, i + 1);
                Sleep(1000);
            }
            for (auto& th : threads) { th.hardware_concurrency(); th.join(); }
        }
    }
    else
    {       
        wprintf(L"Fatal Error: GetModuleHandle failed\n");
        nRetCode = 1;
    }

    return nRetCode;
}

参考:

于 2016-07-14T13:53:03.930 回答