Синхронизация 2х потоков

Есть 2 потока которые выполняют след. функции.

void f2() {
    auto logger = getLogger("log.txt", "f2"); // Log to file with name log.txt. Each message has f2 prefix
    // File output: 23.02.2020 18:06:06; DEBUG; f2(3444): Running a thread
    logger(DEBUG) << "Running a thread";

    double timeSpent = 10.0;
    // File output: 23.02.2020 18:06:06; WARNING; f2(3444): Time spent in the thread: 10.0 seconds
    logger(WARNING) << "Time spent in the thread: " << timeSpent << " seconds";
}

void f3() {
    auto logger = getLogger("log2.txt", "f3");  // Log to file with name log2.txt. Each message has f3 prefix
    // File output: 23.02.2020 18:06:06; INFO; f3(3444): Running a thread
    logger << "Running a thread"; // Default log level INFO

    int i = 123;
    // File output: 23.02.2020 18:06:06; ERROR; f3(3444): My int is 123;
    logger(ERROR) << "My int is " << i;
}

Реализация функции getLogger()

auto getLogger(const char* file, const char* pref = " ")
{
    logclass a(file, pref);
    if (LOGFILE.is_open()) LOGFILE.close();
    LOGFILE.open(file, ios::app);
    return a;
}

В классе logclass перегружены операторы << и ()

ostream& logclass::operator () (loglevels level)
{

    ostream& os = filename ? LOGFILE : cout;
    time_t t = time(0);
    tm* now = localtime(&t);
    lock_guard<mutex> lock(g_lock);

    os << endl << now->tm_mday << "/" << now->tm_mon << "/" << 1900 + now->tm_year;
    os << ", " << now->tm_hour << ":" << now->tm_min << ":" << now->tm_sec << ": ";
    

    switch (level)
    {
    case INFO:

        os << "INFO; ";
        if (prefix) os << prefix;
        os << "(" << this_thread::get_id() << ") ";
        return os;

    case DEBUG:

        os << "DEBUG; ";
        if (prefix) os << prefix;
        os << "(" << this_thread::get_id() << ") ";
        return os;

    case WARNING:

        os << "WARNING; ";
        if (prefix) os << prefix;
        os << "(" << this_thread::get_id() << ") ";
        return os;

    case ERROR:

        os << "ERROR; ";
        if (prefix) os << prefix;
        os << "(" << this_thread::get_id() << ") ";
        return os;
    }
}

template <class T>
void operator << (logclass& a, T smth)
{
    ostream& os = a.filename ? LOGFILE : cout;
    time_t t = time(0);
    tm* now = localtime(&t);
    lock_guard<mutex> lock(g_lock);

    os <<  endl << now->tm_mday << "/" << now->tm_mon << "/" << 1900 + now->tm_year;
    os << ", " << now->tm_hour << ":" << now->tm_min << ":" << now->tm_sec << ": ";
    if (a.prefix) os << "INFO; " << a.prefix << "(" << this_thread::get_id() << ") " << smth;
    else os << "INFO; " << "(" << this_thread::get_id() << ") " << smth;
    
}

Не имею ни малейшего понятия как их правильно синхронизировать, нужна помощь.


Ответы (1 шт):

Автор решения: Chorkov

У вас мутексом защищено не все предложение вывода строк, а только та часть которая находится внутри logclass::operator (). Для того чтобы защитить весь блок вывода, нужно возвращать из logclass::operator () како-то временный объект, тогда в его деструкторе можно будет определить что предложение окончено и снять блокировку. Например:

struct locked_ostream
{
    std::ostream& os;
    std::unique_lock<std::mutex> lock;
    
    template< typename T>
    std::ostream& operator<< ( const T& rhs ) const
    {
        return os<<rhs;
    }
};

locked_ostream logclass::operator () (loglevels level)
{
    std::ostream& os = std::cout;
    std::unique_lock<std::mutex> lock(g_lock);
    
    ... 

    return locked_ostream{ os, std::move(lock) };
}
→ Ссылка