简体   繁体   中英

Writing (logging) into same file from different threads , different functions?

In C++ is there any way to make the writing into file thread safe in the following scenario ?

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

In my application (multi-threaded) , it is likely that foo_one() and foo_two() are executed by two different threads at the same time . Is there any way to make the above thread safe ?

I have considered using the file-lock ( fcntl and/or lockf ) but not sure how to use them because fopen() has been used in the application ( performance reasons ) , and it was stated somewhere that those file locks should not be used with fopen ( because it is buffered )

PS : The functions foo_one() and foo_two() are in two different classes , and there is no way to have a shared data between them :( , and sadly the design is such that one function cannot call other function .

Add a function for logging.
Both functions call the logging function (which does the appropriate locking).

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

    // open("abc.txt");
    // write msg
    // close
}

If you really need a logger, do not try doing it simply by writing into files and perhaps use a dedicated logger, thus separating the concerns away from the code you're writing. There's a number of thread-safe loggers: the first one that comes to mind: g2log . Googling further you'll find log4cplus , a discussion here , even a minimalist one , +1

If the essence of functions foo_one() and foo_two() are only to open the file, write something to it, and close it, then use the same mutex to keep them from messing each other up:

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

Of course, this assumes these are the only writers. If other threads or processes write to the file, a lock file might be a good idea.

You should do this, have a struct with a mutex and a ofstream:

struct parser {
    ofstream myfile
    mutex lock
};

Then you can pass this struct (a) to foo1 and foo2 as a void*

parser * a = new parser();

initialise the mutex lock, then you can pass the struct to both the functions.

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

You can do the same for the foo_two function. This will provide a thread safe means to write to the same file.

Try this code. I've done this with MFC Console Application

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

Referance:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM