Нужно знать, как проверить, завершилась ли введенная строка в потокобезопасном регистраторе на С++.

Я очень новичок в этом и прошу прощения, если мой вопрос не ясен.

Я создал потокобезопасный регистратор на С++. Этот регистратор будет использоваться в большой программе и вызываться из нескольких мест. Я использую синглтон, поэтому есть только один экземпляр регистратора. Этот регистратор выводит в файл и на консоль. Он ведет себя подобно cout; он берет строку из другого файла (при необходимости объединяет ее), сохраняет фрагменты в буфере до тех пор, пока строка не будет завершена, а затем выводит с помощью cout. Строка сохраняется как const char*. Прямо сейчас мьютексы блокируются в одной функции и разблокируются в другой функции (это была моя проблема), которая перегружает оператор endl.

Моя проблема в том, что эта функция (где мьютексы разблокированы) работает только в том случае, если пользователь пишет endl в других файлах, где вызывается регистратор. Мне нужно, чтобы это была универсальная утилита, которая НЕ будет полагаться на то, что пишет пользователь, поскольку пользователь может не использовать endl или может использовать его слишком часто. Теперь мне нужны какие-то средства для моего регистратора, чтобы определить, когда строка (из другого файла) сделана, чтобы он мог очистить буфер. В настоящее время endl похож на ключевое слово, и мне нужны какие-то средства, чтобы заставить его работать без каких-либо ключевых слов.

Сначала я думал, что могу найти какие-то средства для проверки завершающего символа «\ 0» в строке, а затем использовать эту проверку, чтобы узнать, что строка выполнена, а затем очистить буфер. Однако, когда я это делаю, я получаю ошибки за пределами границ.

Спасибо за уделенное время


person joy    schedule 25.08.2010    source источник
comment
Было бы полезно, если бы вы добавили немного исходного кода, например, как вызывается регистратор. Каков общий формат сообщений об ошибках и какой тип вы используете. Судя по вашему вопросу, это может быть std::string, поток или массив символов.   -  person Dennis Miller    schedule 26.08.2010
comment
Регистратор предназначен для обработки как std::string, так и const char*, он обрабатывает оба   -  person joy    schedule 26.08.2010


Ответы (3)


Я не совсем уверен, что понимаю ситуацию, но похоже, что вам нужен прокси:

class LogSingleton
{
public:
    LogSingleton& instance() { /* ... */ }

    void lock(); // lock mutex
    void unlock(); // unlock mutex

    template <typename T>
    friend LogSingleton& operator<<(LogSingleton& pLog, const T& pX)
    {
        // needs to be locked first
        assert(is_locked()); 

        /* output pX however */

        return pLog;
    }
};

class LogProxy
{
public:
    LogProxy()
    {
        // manage lock in proxy
        LogSingleton::instance().lock();            
    }

    ~LogProxy()
    {
        LogSingleton::instance().unlock();            
    }
};

// forward input into the proxy to the log, knowing it's locked
template <typename T>
LogProxy& operator<<(LogProxy& pProxy, const T& pX)
{
    LogSingleton::instance() << pX;

    return pProxy;
}

// now expose proxy
typedef LogProxy log;

И вы бы сделали это:

log() << "its locked now" << "and the temporary will die" << "here ->";

Блокировка выполняется в конструкторе и деструкторе, а деструктор вызывается в конце.


Как правильно отмечает Тони, это излишне долго удерживает блокировку. Блокировка нужна только для "окончательного" вывода в LogSingleton. Представьте себе это:

log() << "this next function takes 5 minutes"
        << my_utterly_crappy_function() << "ouch";

Ничего не регистрируется, но мьютекс заблокирован в течение длительного времени. Лучше было бы буферизовать вывод, а затем выводить все сразу:

class LogProxy
{
public:
    ~LogProxy()
    {
        // manage lock in proxy
        LogSingleton::instance().lock();

        // no-throw, or wrap mutex use in a scoped-lock
        LogSingleton::instance() << mBuffer.rdbuf();

        LogSingleton::instance().unlock();            
    }

    // buffer output
    template <typename T>
    friend LogProxy& operator<<(LogProxy& pProxy, const T& pX)
    {
        mBuffer << pX;

        return pProxy;
    }

private:
    std::ostringstream mBuffer;
};

Теперь никакие блокировки не устанавливаются до тех пор, пока буфер не будет готов к выводу.

person GManNickG    schedule 25.08.2010
comment
Если приложение регистрирует много данных, это решение может серьезно снизить производительность. Хотя это менее эффективно для легкого ведения журнала или когда один поток выполняет почти все ведение журнала, более масштабируемым является объединение вывода в ostringstream, а затем блокировка синглтона только тогда, когда он готов к записи. (В некоторых системах будет выполняться одна запись () для потока, в то время как поток/файл содержит собственную блокировку мьютекса, что гарантирует атомарность, но это не переносимо). - person Tony Delroy; 26.08.2010

Как правило, блокировать мьютекс в одной функции и разблокировать в другой — плохая идея. Он должен быть заблокирован и разблокирован в одной и той же функции.

Я создал что-то подобное, и обычно я создал класс C++ с именем Error.

Таким образом, пользователь создает объект Error, и этот объект ошибки обрабатывает все элементы завершения. Затем объект ошибки отправляется в очередь ErrorLogger, и регистратор ошибок завершает работу, когда очередь ErrorLogger пуста. Тогда вам не придется беспокоиться о мьютексах, потому что ErrorLogger успевает обработать вне очереди.

person Dennis Miller    schedule 25.08.2010
comment
Деннис, будучи новичком в этом, я рассмотрю предложенное вами решение и посмотрю, сработает ли оно для меня. Спасибо за ваш ответ. Однако я не знаю, смогу ли я избавиться от мьютексов. Это очень сложный регистратор, он имеет много дополнительных функций, которые я не упомянул выше. Но их нужно делать между блокировкой и разблокировкой. Я использую мьютексы в другом месте, чтобы гарантировать, что регистратор является потокобезопасным и функциональным с этими функциями. Тем не менее, мне все еще нужны какие-то средства, чтобы узнать, когда строка завершена. Как я могу проверить, сделана ли строка без какого-либо ключевого слова? - person joy; 26.08.2010
comment
Мне нужно больше узнать о строке. Это просто std::string, массив символов или поток? Я предполагаю, что вы используете мьютексы, чтобы предотвратить публикацию текущего сообщения об ошибке другим потоком, пока вы записываете старое в файл правильно? - person Dennis Miller; 26.08.2010
comment
если вы читаете из файла, можете ли вы отслеживать длину каждой строки в этом файле? std::string строка; getline (входной файл, строка); int lengthInCharacters = строка.длина() - person Dennis Miller; 26.08.2010

Проверьте https://web.archive.org/web/1/http://articles.techrepublic%2ecom%2ecom/5100-10878_11-5072104.html

Идея состоит в том, чтобы создать локальные для потока «прокси», которые будут вызывать фактическую функцию ведения журнала, безопасную для потоков.

person phadej    schedule 25.08.2010