线程安全流和流操纵器

发布于 2024-10-29 10:10:19 字数 1333 浏览 3 评论 0 原文

我正在尝试编写一个线程安全的记录器类,以便我可以执行与 cout 完全相同的操作,但具有线程安全性。

这是记录器类(仍在处理所需的锁定类型)

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<

  2. int i = 10;
    log<<字符串(“i =”)<<我<< endl;

以下是编译错误。

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++ 的一些重要概念。请告诉我这是什么? 我的要求可以实现吗

谢谢 基兰

I am trying to write a thread safe logger class so that i can do the exact same as with cout but with thread safety.

here is the logger class (still working on the type of lock required)

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;

};

The poblem is I cannot do the following

  1. log<<"asdfg";
    I have to instead do
    log<<string("asdfg");

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

following is the compilation error.

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’

So I guess i am missing some important concept of C++. Please let me know what it is?
Is my requirement even achievable

thanks,
Kiran

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

尐偏执 2024-11-05 10:10:19

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

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 通过在每条消息后始终刷新流来单独处理。

Note that your logger class is still not thread safe:

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

There is nothing stopping this thread from getting preempted by another another thread printing to logger and producing something like:

i = i = 12

Instead of:

i = 1
i = 2

If you have a compiler with variadic templates, here's one way of fixing this:

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

I.e. the mutex is locked until all arguments for given log message are processed. std::endl is handled separately by always flushing the stream after every message.

剧终人散尽 2024-11-05 10:10:19

您的问题是 logger 不是流,因此通常的流运算符将不起作用,只能使用您定义的单个流运算符。

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

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

现在 log << “你好!” << endl; 会起作用。

能够链接多个<<像流一样一起操作,您必须为记录器定义所有运算符(就像流一样)。

Your problem is that logger isn't a stream, so the usual stream operators will not work, just the single one you have defined.

You can get endl to work by defining it yourself:

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

Now log << "Hello!" << endl; will work.

To be able to chain several << operations together like the streams do, you will have to define all the operators for the logger (just like the streams do).

臻嫒无言 2024-11-05 10:10:19

我认为你在太低的水平上引入同步。要理解原因,假设线程 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();

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

I think you are introducing synchronization at too low a level. To understand why, assume thread 1 executes:

log << "This is " << "my " << "log message" << endl;

while thread 2 executes:

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

In such a case, the log file (or console output) may contain interleaved messages, for example:

This is Hello, my World!
log message

To avoid this problem, your application will have to construct
an entire message as a single string, and only then pass that string to a logger object. For example:

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

If you take this approach, then your logger class does not need to overload operator<< for endl and multiple types.

自我难过 2024-11-05 10:10:19

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

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

I tested your program with some simplifications as follows, and it compiles and runs fine, which means that the problem is probably elsewhere:

#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";
}
在你怀里撒娇 2024-11-05 10:10:19

在深入研究 iostreams 并根据 Bo Persson 的提示后,我认为我找到了更好的解决方案,因为我不需要为每个 ios 操纵器分别编写一个函数。所以这里是

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

iostreams 和应用程序的解释搜索。

这是完整的 boost:: 线程安全实现(可能需要一些重构和优化),使用 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;

After digging into iostreams and with hints from Bo Persson, I think i found a better solutions since I dont need to write a function each for each ios manipulator. So here it is

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

For an explanation search for iostreams and applicators.

Here is the complete boost::thread safe implementation (requires some refactoring and optimization probably) using some hints from 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;
}

use it like this

LOG::logger logOut (LOG::STDOUT);
logOut<<"Hello World\n"<<LOG::flush;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文