线程安全流和流操纵器

时间:2011-04-03 07:24:23

标签: c++ multithreading iostream

我正在尝试编写一个线程安全的记录器类,这样我就可以使用与cout完全相同但具有线程安全性。

这里是logger类(仍在处理所需的锁类型)

class logger {

public:

    logger(LOGGER::output_type type);

    logger(const logger& orig);

    virtual ~logger();

    template <typename T>
    logger & operator << (const T & data){
        boost::mutex::scoped_lock io_mutex_lock(io_mutex);
        (*out)<<data;
        return *this;
    }

private:
    static boost::mutex io_mutex;
    std::ostream * out;

};

问题是我无法做到以下

  1. log<<"asdfg";
    我不得不这样做 log<<string("asdfg");

  2. int i = 10;
    log<<string ("i = ") << i << endl;

  3. 以下是编译错误。

    gcc.compile.c++ src/simpleThread/bin/gcc-4.4.5/debug/simpleThread.o
    src/simpleThread/simpleThread.cc: In function ‘int main()’:
    src/simpleThread/simpleThread.cc:28: error: no match for ‘operator<<’ in ‘((logger*)logOut.logger::operator<< [with T = char [18]](((const char (&)[18])"fibonacci thread ")))->logger::operator<< [with T = int](((const int&)((const int*)(& i)))) << std::endl’
    

    所以我想我错过了一些重要的C ++概念。请让我知道它是什么? 我的要求是否可以实现

    感谢, 基兰

5 个答案:

答案 0 :(得分:5)

请注意,您的记录器类仍然不是线程安全的:

int i = 10;
log <<string ("i = ") << i << endl;

没有什么可以阻止这个线程被另一个打印到记录器的线程抢占并产生类似的东西:

i = i = 12

而不是:

i = 1
i = 2

如果你有一个带有可变参数模板的编译器,这里有一种解决方法:

#include <ostream>
#include <mutex>

inline void sub_print(std::ostream&) {}

template <class A0, class ...Args>
void
sub_print(std::ostream& os, const A0& a0, const Args& ...args)
{
    os << a0;
    sub_print(os, args...);
}

std::mutex&
io_mut()
{
    static std::mutex m;
    return m;
}

template <class ...Args>
void
log(std::ostream& os, const Args& ...args)
{
    std::lock_guard<std::mutex> _(io_mut());
    sub_print(os, args...);
    os.flush();
}

#include <iostream>

int main()
{
    int i = 10;
    log(std::cout, "i = ", i, '\n');
}

即。互斥锁被锁定,直到处理给定日志消息的所有参数。通过在每条消息之后始终刷新流来单独处理std::endl

答案 1 :(得分:4)

你的问题是logger不是一个流,所以通常的流操作符不起作用,只是你定义的单个操作符。

您可以通过自己定义来endl开始工作:

inline logger& endl(logger& log)
{
    // do something to *out
    return log;
}

现在log << "Hello!" << endl;可以使用。

能够链接几个&lt;&lt;像流一样操作,你必须为记录器定义所有操作符(就像流一样)。

答案 2 :(得分:4)

我认为你引入的同步水平太低了。要理解原因,假设线程1执行:

log << "This is " << "my " << "log message" << endl;
线程2执行时

log << "Hello, " << "World!" << endl;

在这种情况下,日志文件(或控制台输出)可能包含交错消息,例如:

This is Hello, my World!
log message

要避免此问题,您的应用程序必须构建 整个消息作为单个字符串,然后才将该字符串传递给记录器对象。例如:

ostringstream msg;
msg << "This is " << "my " << "log message" << endl; 
log << msg.str();

如果您采用这种方法,那么您的记录器类不需要为operator<<和多种类型重载endl

答案 3 :(得分:1)

我通过以下一些简化测试了您的程序,它编译并运行正常,这意味着问题可能在其他地方:

#include <iostream>

class logger {

public:

//    logger(LOGGER::output_type type);

    logger(std::ostream& os): out(&os) {}

    ~logger() {}

    template <typename T>
    logger & operator << (const T & data){
//        boost::mutex::scoped_lock io_mutex_lock(io_mutex);
        (*out)<<data;
        return *this;
    }

private:
//    static boost::mutex io_mutex;
    std::ostream * out;

};

int main()
{
        logger log(std::cout);
        log << std::string("hello ");
        log << "world\n";
}

答案 4 :(得分:1)

在深入了解iostream并使用Bo Persson的提示后,我认为我找到了更好的解决方案,因为我不需要为每个ios操纵器编写一个函数。所以这就是

    logger& operator<<(std::ostream& (*pf)(std::ostream&)) {
        (*out)<<pf;
        return *this;
    }

搜索iostreams和涂抹器的解释。

这是完整的boost :: thread安全实现(可能需要一些重构和优化),使用Ciaran-Mchale

中的一些提示
/* 
 * File:   logger.h
 * Author: Kiran Mohan
 *
 */

#ifndef LOGGER_H
#define LOGGER_H

#include <boost/thread.hpp>
#include <iostream>

namespace LOG {

    enum output_type {
        STDOUT,
        STDERR
    };

    /**
     * a thread safe logger to print to stdout or stderr
     */
    class logger {
    public:

        logger(LOG::output_type type);

        logger(const logger& orig);

        virtual ~logger();

        template <typename T>
        logger & operator <<(T data) {
            /* Takes any data type and stores in a stringstream
             */
            (*oss) << data;
            return *this;
        }

        logger & operator<<(std::ostream& (*pf)(std::ostream&)) {
            // for stream manipulators
            (*oss) << pf;
            return *this;
        }

        logger & operator<<(logger & (*pf)(logger &)) {
            //applicator - mainly calling the print function;
            return pf(*this);
        }

        friend logger & flush(logger & l);

        logger & print() {
            boost::mutex::scoped_lock io_mutex_lock(io_mutex);
            (*out) << oss->str() << std::flush;
            delete oss;
            oss = new std::ostringstream;
            return *this;
        }

    private:

        static boost::mutex io_mutex;
        std::ostream * out;

        std::ostringstream * oss;

    };

    logger & flush(logger & l);

};
#endif  /* LOGGER_H */
/* 
 * File:   logger.cc
 * Author: aryan
 * 
 */

#include <boost/thread/pthread/mutex.hpp>
#include <iostream>
#include "logger.h"

using namespace LOG;
boost::mutex logger::io_mutex;

logger::logger(LOG::output_type type) {
    if (type == LOG::STDOUT) {
        out = &std::cout;
    } else {
        out = &std::cerr;
    }
    oss = new std::ostringstream;
}

logger::logger(const logger& orig) {
    out = orig.out;
}

logger::~logger() {
    delete oss;
}

logger & LOG::flush(logger & l) {
    l.print();
    boost::this_thread::yield();
    return l;
}

像这样使用

LOG::logger logOut (LOG::STDOUT);
logOut<<"Hello World\n"<<LOG::flush;