2016-10-04 2 views
0

Я работаю над многопоточным кодом на C++, где его можно запустить либо как exe, либо XLL Add-on для Excel, в любом случае он будет запускаться в нескольких потоках.Какова хорошая практика регистрации в многопоточности?

Какая хорошая практика ведения каротажа?

Там в похожую на поток программ Logging in multithreading environment and testing C#, что предполагает:

Wrap экземпляр протоколирования в поточно-одноплодной. Не используйте двойной контроль блокировки! Кроме того, он, вероятно, имеет смысл использовать библиотеку каротаж как log4net или Enterprise Library 5.

Интересно, если это до сих пор относится к программам C++?

Некоторые говорят, что Singleton is evil, но кажется, что лоджины являются допустимым случаем для этого шаблона?

Каковы хорошие библиотеки ведения журнала, которые будут заботиться о проблеме буфера потока, поэтому по файлам журналов достаточно восстановить сценарии до конца?

Использование Singleton для ведения журнала из нескольких потоков, приведет ли это к снижению производительности? Должен ли использоваться мьютекс/замок?

Я работаю над средами Windows.

+0

Singleton имеет свои действующие устройства (например, регистраторы). –

+0

Независимо от singleton-or-not: 'регистрация из нескольких потоков ... Будет ли использоваться mutex/lock?' Да. – deviantfan

+0

как насчет производительности, вызванной синхронизацией? @ πάνταῥεῖ – athos

ответ

1

Как вы делаете запись из многопотокового без использования Singleton?

Я нахожу этот вопрос любопытным.

ключевых частей доступа к моему барану основы-логу (Круговой стиль) является:


  • Я часто создать журнал в конструкторе некоторого экземпляра класса, который занимает центральное место в какой продолжается. После создания любой поток имеет доступ к журналу через имя. В следующем случае «M_dtbLog».

    if (0 == M_dtbLog) { M_dtbLog = новый DTB :: Log (1 * 60 * 1000, 1024 * 256 * 2); dtbAssert (0! = M_dtbLog); }

обновления 10/06 10:45 - забыл упомянуть об этом dtblog был создан до 2011 года, так что нет зОго :: нити. Нити были потоками Posix.

  • В любом коде, где вы хотите получить доступ к журналу:

1) тянуть в коде

#include "../../bag/src/dtb_log.hh" 

2) тогда, я добавил макрос для удобства (если вы должен). для иллюстрации:

//  several years ago (in a much more c-style approach), 
#define DBG if(M_dtbLog) (void)M_dtbLog->enq 

2b) тогда я также добавил:

// the compiler checks many more things for printf does 
// use this simple #define temporarily to test your DBG's 
#if (0) 
#undef DBG 
#define DBG if(M_dtbLog) (void)::printf 
#endif 

3) «ДБГ» в качестве кода инжектор теперь доступен, и каждый вызов подтверждает, что дие доступно. Пример:

void LMBM::Node:init(void) 
{ 
    if(m_next) 
     m_next->init(); 
    //... 

    DBG("m_semIn %2d = %p\n", m_nodeId, m_semIn); // <<<<<<<< 

обновления 10/06 10:45 - забыл упомянуть этот узел имеет свои собственные нити и ввод семафор, таким образом, статический M_dtbLog. Настройки по умолчанию создают 10 узлов (и 10 потоков и семафоров). Каждый из этих Узел имеет прямой доступ к статическому M_dtbLog.

4) Совсем недавно я использую то, что я считаю более простым подходом C++ ... для обсуждения в другое время.

5) Несколько раз проще, особенно для модульного испытания. Здесь я иногда использую std :: stringstream (который не является потокобезопасным). Просто нужно помнить, чтобы ограничить, насколько он попадает в код к концу задачи.

6) Для производства, когда Log не требуется, я просто не создаю экземпляр журнала, если не нужен (и обычно это не так). Но я оставляю инструкции DBG в коде, когда требуется отладка.


В любом файле/класс, в котором вы хотите использовать этот журнал, независимо от того, какой поток, просто добавить #include, и #define и тем не менее многие из DBG систем вы хотите.

В настоящее время нет поддержки для измерения проблем с производительностью. Он пишет в баран, и для моих целей было достаточно быстро.

Эксклюзивный контроль доступа (для доступа к нескольким потокам) находится в реализации dtb_log.

В моей версии Linux этого кода я использую Posix Process Semaphore в локальном режиме (т. Е. Не обрабатывается совместно, а не мьютексом или std :: mutex).

Общая работа по созданию строки и ожиданию времени и идентификатора потока и т. Д. Выполняется в автоматической памяти (обычно называемой стеком), так что потоки (каждая из которых имеет собственный стек) не могут мешать каждому Другие.

Строка в автоматической памяти затем помещается в буфер круглой памяти. (с использованием частного метода DTB :: Log :: enqRrq()) dtb_log может использоваться любым потоком. Критический раздел используется типичным способом. 'enqRrq' составляет около 12 строк кода и просто перемещает символы из одного баффа в циклический бафф.

обновление 10/6 - фрагмент кода -


После кода, чтобы ввести метку времени, идентификатор потока, и сообщение и т.д.

// buff 1 now ready to install 
enqRrq(buff1, retVal); // install text into round-robin queue 

Далее передать новую запись журнала в буфер кругло-розового типа

void DTB::Log::enqRrq(const char* buff, size_t sz) 
{ 
    DTB::ProcessSemCritSect critSect(m_mutex); 

    dtbAssert(0 == m_rrBuff[m_nextIn]); // code broken 

    // move char from buff into m_rrBuff 
    { 
     { 
     bmpNextIn(); // leave one null between records 

     for (size_t i = 0; i < sz; i += 1) // insert sz chars of buff 
     { 
      m_rrBuff[m_nextIn] = buff[i]; 
      bmpNextIn();      // m_rrBuff is round-robin 
     } 
     m_rrBuff[m_nextIn] = 0; // insert null char at end of m_rrBuff 
     } 
    } 

    uint32_t lni = m_nextIn + 1; // lni starts after null we just inserted 
    if (lni >= m_rrBuffSize) lni = 0; // round-robin 

    // stomp 0's to end of oldest message 
    { 
     while (0 != m_rrBuff[lni]) // while in middle of oldest message 
     { 
     m_rrBuff[lni++] = 0; // stomp on oldest message (to its end) 
     if (lni >= m_rrBuffSize) lni = 0; // round-robin 
     } 
    } 
} 

Мне довелось найти несколько примеров такого выхода журнала. Извините за длину строки, самая большая из них - 160 символов.

Обратите внимание, что это последние 5 секунд журнала, который я нашел.

  • retrieve_shelf_inventory(), по-видимому, раз в секунду. (обычно повторяющиеся записи журнала подавляются, после того, как они обертываются и топаются на то, что вы хотели видеть, пользователем, а не журналом)

  • последняя секунда показывает обновление для нескольких светодиодов и офисных сигнализаций.

  • pem_cm, другой слой кода, [блок потоков], отправляет команды, которые запускают эти записи журнала.

  • В этой системе работает около 1000 потоков.
    И около 100 разработчиков, пасущих код.

    05:27:41 0x010117 262,816 mcu_t :: retrieve_shelf_inventory() размер = 91 5:27:41 0x010117 262,816 --mcu: sdd_io2 (муравей = 210 (SDD_GET_SHELF_PHY_INV) отправитель = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 основных = 1, 519, (1109792 нас)

    05:27:42 0x010117 263,928 mcu_t :: retrieve_shelf_inventory() размер = 91 5:27:42 0x010117 263,928 --mcu: sdd_io2 (муравей = 210 (SDD_GET_SHELF_PHY_INV) отправитель = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 основных = 1, 520, (1111872 нас)

    05:27:43 0x010117 265,040 mcu_t :: retrieve_shelf_inventory() размер = 91 05 : 27: 43: 0x010 117 265,040 --mcu: sdd_io2 (муравей = 210 (SDD_GET_SHELF_PHY_INV) отправитель = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 основных = 1, 521, (1111873 нас)

    05:27:44 0x010117 266,152 mcu_t :: retrieve_shelf_inventory() size = 91 05:27:44 0x010117 266.152 --mcu: sdd_io2 (ant = 210 (SDD_GET_SHELF_PHY_INV) sender = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 major = 1, 522, (1111883 нас)

    05:27:44 0x010117 266,152 mcu_t :: sdd_control_led (INDX = 13, цвет = 1, действие = 1) = 0 05:27:44 0x010117 266,152 --mcu: sdd_io2 (муравей = 20 (SDD_LED_CONTROL) sender = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 major = 1, 523, (48 us)

    05:27:44 0x010117 266.152 mcu_t :: sdd_control_led (indx = 14, color = 1, action = 1) = 0 05:27:44 0x010117 266.152 --mcu: sdd_io2 (муравей = 20 (SDD_LED_CONTROL) отправитель = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 главная = 1, 524, (19 нас)

    05:27:44 0x010117 266,152 mcu_t: : sdd_control_led (indx = 15, color = 2, action = 1) = 0 05:27:44 0x010117 266.152 --mcu: sdd_io2 (ant = 20 (SDD_LED_CONTROL) отправитель = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 основных = 1, 525, (19 нас)

    5:27:44 0x010117 266,152 mcu_t :: sdd_control_led (INDX = 12, цвет = 0, действие = 0) = 0 5:27:44 0x010117 266,152 - -mcu: sdd_io2 (ant = 20 (SDD_LED_CONTROL) sender = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 major = 1, 526, (21 us)

    05:27:44 0x010117 266.152 pci2: blsr: office_alarms: change() aud_CR.vis_CR.aud_MJ.vis_MJ.aud_MN.vis_MN 0000003f 05:27:44 0x010117 266.152 --mcu: sdd_io2 (ant = 35 (SDD_SET_ALARM) отправитель = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 основных = 1, 527, (26 нас)

    05:27:44 0x010117 266,200 mcu_t :: sdd_control_led (INDX = 13, цвет = 1, действие = 1) = 0 5:27:44 0x010117 266,200 --mcu: sdd_io2 (муравей = 20 (SDD_LED_CONTROL) отправитель = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 основных = 1, 528, (49 нас)

    05:27:44 0x010117 266.200 mcu_t :: sdd_control_led (indx = 14, color = 1, action = 1) = 0 05:27:44 0x010117 266.200 --mcu: sdd_io2 (ant = 20 (SDD_LED_CONTROL) отправитель = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 major = 1, 529, (20 us)

    05:27:44 0x010117 266.200 mcu_t :: sdd_control_led (indx = 15, color = 2, action = 0) = 0 5:27:44 0x010117 266,200 --mcu: sdd_io2 (муравей = 20 (SDD_LED_CONTROL) отправитель = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 главная = 1, 530, (19 нас)

    05 : 27: 44 0x010117 266.200 mcu_t :: sdd_control_led (indx = 12, color = 0, action = 0) = 0 05:27:44 0x010117 266.200 --mcu: sdd_io2 (ant = 20 (SDD_LED_CONTROL) отправитель = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 major = 1, 531, (20 us)

    05:27:44 0x010117 266.200 pci2: blsr: office_a larms: change() aud_CR.vis_CR.aud_MJ.vis_MJ.aud_mn.vis_mn 0000001b 05:27:44 0x010117 266.200 --mcu: sdd_io2 (ant = 35 (SDD_SET_ALARM) sender = 0001020e (pem_cm), sec_id = 4088C04080000000) eqType = 35 основных = 1, 532, (25 нас)

    513 линий, 55287 байт (m_size = 65536) шоу, 55287


обновление 11/04

Некоторые говорит, что Синглтон злой, но кажется, что лоджины - действительный случай для этот узор?

Я использовал несколько singleton, но в итоге нашел singleton's, чтобы добавить значение. У этого есть запоминающееся имя, поэтому его легче запомнить, поэтому, возможно, он более читабельен для других, и поэтому вы видите больше об этом, чем другие шаблоны (я думаю). Но даже когда я использую идеи для доступа и управления hw, из которых есть только один ... imho, singleton не упрощал и не ускорял ничего.

Я еще не видел код, где синглтон «обеспечивает» безопасность потоков. Я считаю, что в книге шаблонов (в которой опубликован шаблон) есть утверждение, в котором говорится, что ни один из шаблонов не является потокобезопасным. (Но прошло какое-то время с тех пор, как я посмотрел.) Возможно, C# singleton - это нечто другое.

Когда автор, который вы цитировали, сказал: «Оберните свой экземпляр журнала в потокобезопасном синглетоне». Я полагаю, что он предлагал не более a) использовать одноэлементный режим и b) добавлять безопасность потока в одноэлементный. Синглтон не обеспечивает безопасность потока, и безопасность потоков не обеспечивает одноэлементных функций.


обновление 11/04

Использование Singleton протоколирование из нескольких потоков, что бы привести к снижению производительности? Должен ли использоваться мьютекс/замок?

Как я уже говорил выше, я не видел идеи, что одноэлемент обеспечивает безопасность потоков. Имхо это не так.

Производительность всегда получает хитон с синхронизацией нескольких потоков, обращающихся к общему ресурсу. Приведенный выше код создает критический раздел с использованием семафора POSIX. Эта реализация выглядит примерно так:

DTB::ProcessSemCritSect::ProcessSemCritSect(ProcessSemaphore* a_processSem) : 
    m_processSem (a_processSem) 
{ 
    assert(nullptr != m_processSem); 
    int wait_status = m_processSem->wait(); // block if already locked 
    assert(DTB::ProcessSemaphore::SUCCESS == wait_status); 
} 


DTB::ProcessSemCritSect::~ProcessSemCritSect(void) 
{ 
    assert(nullptr != m_processSem); 
    int post_status = m_processSem->post(); // un-block any waiting thread 
    assert(DTB::ProcessSemaphore::SUCCESS == post_status); 
    m_processSem = 0; 
} 

Мои тесты показывают, что два шага ожидания() и пост() очень быстро, в моей системе < 25 нс.

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

Темы (в разных ядрах, возможно), которые сталкиваются в этом стиле критического раздела, могут принимать до 12 000 нс для контекстного переключателя в моей системе. Ваша производительность будет отличаться.

+0

@ douglas-o-moen, где я могу найти этот круговой стиль ram-based-log? – athos

+0

@athos - Я больше не смотрел мимо своих собственных библиотек больше десятилетия. На моей работе я помню только один случай, когда член команды приносил свой любимый инструмент регистрации, у меня создалось впечатление, что это тоже обычай, но не круговая память на основе памяти. --- С одним кратким поиском google я обнаружил, что «Log4cpp - это библиотека классов C++ для гибкого ведения журнала в файлах, syslog, IDSA и других пунктах назначения. Она смоделирована после библиотеки Java Log4j, оставаясь как можно ближе к их API». Может быть, ssd достаточно быстро? Были и другие библиотеки. Не найдено ни одного ключевого слова round-robin. –

+0

После сегодняшнего перечитания я вижу, что я не смог подчеркнуть, как реализация журнала достигла безопасности потоков. a) код для ввода метки времени, идентификатора потока и сообщения выполняется в стеке стека, а не в какой-либо общей памяти, и b) строка «DTB :: ProcessSemCritSect critSect (m_mutex)»; знаменует критический раздел (начало), в котором обновляется циклический буфер, таким образом синхронизируя любой поток доступа к m_rrBuff и элементам управления. –

Смежные вопросы