Комментарии 11
А почему синхронизация с помощью sleep а не с помощью мутексов, например? sleep'ом проблема не решается, а скрывается. Если где-то есть возможность хорошо загрузить процессор и выполнить heap spray, то подобная синхронизация может привести к слепому выполнению кода на сервере.
Там нет возможности хорошо загрузить процессор (загружены где-то 3 cores из 16 (или 8? — уже не помню) процентов на 40 ).
Загрузка по входному потоку распределяется на 2 процессора, загрузка по запросам ограничена сверху.
Использовать в данном случае mutex мне показалось неэлегантным, тем более что в явном виде мютексов там почти нет (используется только один).
Загрузка по входному потоку распределяется на 2 процессора, загрузка по запросам ограничена сверху.
Использовать в данном случае mutex мне показалось неэлегантным, тем более что в явном виде мютексов там почти нет (используется только один).
Ее нет, потому что прямо сейчас никто ее не создал. Это не значит, что не придет ботнет из пары сотен тысяч узлов и ее не создаст. Не значит, что завтра не упадет промежуточный маршрутизатор и не прилетят потом разом данные за пол-дня.
Вы может и уйдете — а код останется, нельзя оставлять в коде такие грабли.
Есть механизмы синхронизации между потоками — надо их использовать, иначе потом плавающие race conditions будут ловить годами, вспоминая о вас с нежностью.
Вы может и уйдете — а код останется, нельзя оставлять в коде такие грабли.
Есть механизмы синхронизации между потоками — надо их использовать, иначе потом плавающие race conditions будут ловить годами, вспоминая о вас с нежностью.
Мне кажется, что это бомба замедленного действия. Если какие-то тайминги поплывут и sleep не спасёт, а вы уже будете далеко или забудите об отказе от mutex, то поиск причины падения сервиса может быть очень долгим и «увлекательным».
Если все взвесить, это не такой простой вопрос.
Поток, который что-то считает в структуре по этому указателю, делает маленькие кратковременные вычисления очень часто (несколько тысяч в секунду). Каждый раз захватывать мютекс, конечно же, можно (можно и на несколько порядков больше), но все это дается не бесплатно. Поэтому, собственно, возникает желание написать wait-free алгоритм работы.
Задача сериализующего потока потока — забрать текущий указатель, и подменить другим. Атомарность замены одного на другое не решает проблему дальнейшего использования указателей. Тут можно решить тремя путями (спасибо Александреску): подсчет ссылок, подождать достаточное время (выбор автора статьи), и hazard pointers. Для подсчета ссылок понадобится DCAS (чего нет у x86), или же CAS2 (использовать старшие 16 бит указателя для счетчика). А для реализации hazard pointers нужно уметь проверять что в пользовании у других потоков, это тоже не тривиально в плане синхронизации.
Тут можно было бы предложить сделать отсечки timestamp между подсчетами в одном потоке, и проверять их сериализующем: сериализующий поток подменяет указатель, и делает свою отсечку, делаем смещение на безопасный интервал (в разных ядрах может не clocks), и ждем когда у другого потока timestamp станет больше нашего. Это будет нам говорить, что при последнем обращение к указателю в хештаблице другой поток «увидел» новое значение. После этого можно сериализовать данные и освобождать память.
Поток, который что-то считает в структуре по этому указателю, делает маленькие кратковременные вычисления очень часто (несколько тысяч в секунду). Каждый раз захватывать мютекс, конечно же, можно (можно и на несколько порядков больше), но все это дается не бесплатно. Поэтому, собственно, возникает желание написать wait-free алгоритм работы.
Задача сериализующего потока потока — забрать текущий указатель, и подменить другим. Атомарность замены одного на другое не решает проблему дальнейшего использования указателей. Тут можно решить тремя путями (спасибо Александреску): подсчет ссылок, подождать достаточное время (выбор автора статьи), и hazard pointers. Для подсчета ссылок понадобится DCAS (чего нет у x86), или же CAS2 (использовать старшие 16 бит указателя для счетчика). А для реализации hazard pointers нужно уметь проверять что в пользовании у других потоков, это тоже не тривиально в плане синхронизации.
Тут можно было бы предложить сделать отсечки timestamp между подсчетами в одном потоке, и проверять их сериализующем: сериализующий поток подменяет указатель, и делает свою отсечку, делаем смещение на безопасный интервал (в разных ядрах может не clocks), и ждем когда у другого потока timestamp станет больше нашего. Это будет нам говорить, что при последнем обращение к указателю в хештаблице другой поток «увидел» новое значение. После этого можно сериализовать данные и освобождать память.
Как-то так, например:
статические данные:
мутекс (постоянный),
указатель на структуру {
serial number
хэштейбл
счетчик
kqueue, pipe или futex — любой из них
}
В потоке, который что-то пишет:
loop{
если сохраненный в потоке serial number отличается от текущего {
— блокирую мутекс
уменьшаю сохраненный в потоке счетчик.
Если сохраненный в потоке счетчик равен 0 — шлю событие через футекс, очередь или пайп
сохраняю локально в потоке текущие serial number и указатель на структуру
увеличиваю сохраненный в потоке счетчик
— разблокирую мутекс
}
использую сохраненный в потоке хэштейбл
}
в потоке, который сериализует/ротейтит/удаляет:
loop {
— блокирую мутекс
сохраняю старые указатели на хэш, счетчик, queue/pipe/futex
создаю новые хэш, счетчик, queue/pipe/futex
инкременчу serial
— разблокирую мутекс
пока(счетчик!=0)жду событие от kqueue/pipe/futex
сериализую/освобождаю старые хэш, счетчик, queue/pipe/futex
жду остаток секунды
}
итого — раз в секунду одна блокировка мутекса на каждый поток + раз в секунду одно создание+запись+чтение kqueue pipe или futex. По-моему никаких особых нагрузок нет, лишних саящих потоков тоже нет.
Кстати, sleep это системный вызов, вполне сравнимый с мутексом по тяжести.
статические данные:
мутекс (постоянный),
указатель на структуру {
serial number
хэштейбл
счетчик
kqueue, pipe или futex — любой из них
}
В потоке, который что-то пишет:
loop{
если сохраненный в потоке serial number отличается от текущего {
— блокирую мутекс
уменьшаю сохраненный в потоке счетчик.
Если сохраненный в потоке счетчик равен 0 — шлю событие через футекс, очередь или пайп
сохраняю локально в потоке текущие serial number и указатель на структуру
увеличиваю сохраненный в потоке счетчик
— разблокирую мутекс
}
использую сохраненный в потоке хэштейбл
}
в потоке, который сериализует/ротейтит/удаляет:
loop {
— блокирую мутекс
сохраняю старые указатели на хэш, счетчик, queue/pipe/futex
создаю новые хэш, счетчик, queue/pipe/futex
инкременчу serial
— разблокирую мутекс
пока(счетчик!=0)жду событие от kqueue/pipe/futex
сериализую/освобождаю старые хэш, счетчик, queue/pipe/futex
жду остаток секунды
}
итого — раз в секунду одна блокировка мутекса на каждый поток + раз в секунду одно создание+запись+чтение kqueue pipe или futex. По-моему никаких особых нагрузок нет, лишних саящих потоков тоже нет.
Кстати, sleep это системный вызов, вполне сравнимый с мутексом по тяжести.
Все уважающие себя сайты должны добавить пару десятков аналитики на сайт, а все уважающие себя пользователи — блокировать.
Рекомендую (для FF):
* RequestPolicy
* Ghostery

Рекомендую (для FF):
* RequestPolicy
* Ghostery

И если поток, который “обогащает” записи в tail (геолокация по ip, мапирование OS, браузеров), запишет в удаленную структуру данных, будет бобо. Также известное как SEGFAULT.
Я вас, возможно, немного расстрою. Но «бобо» будет SEGFAULT'ом далеко не всегда. Более точное описание: undefined behavior. В вашем случае SEGFAULT будет редкой удачей, позволяющей относительно быстро понять в чём проблема.
Есть такая практика под названием «до первого столба». Это когда в коде есть известная необрабатываемая ситуация, которая валидна, но, как кажется разработчику, не должна возникнуть никогда, на возникновение которой стоит некий guard, подающий недвузначный сигнал. Если бы у вас SEGFAULT был гарантированно, то можно было бы считать, что вы применяете эту практику, сэкономив на кодировании синхронизации. В вашем же случае, вы, скорее всего, даже не узнаете, что «бобо» случилось.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Зачем коту хвост: realtime статистика в условиях средней видимости