Как вы делаете запись из многопотокового без использования 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 нс для контекстного переключателя в моей системе. Ваша производительность будет отличаться.
Singleton имеет свои действующие устройства (например, регистраторы). –
Независимо от singleton-or-not: 'регистрация из нескольких потоков ... Будет ли использоваться mutex/lock?' Да. – deviantfan
как насчет производительности, вызванной синхронизацией? @ πάνταῥεῖ – athos