Подтвердить что ты не робот

Накладные расходы на мьютексы pthread?

Я пытаюсь сделать С++ API (для Linux и Solaris) потокобезопасным, чтобы его функции можно было вызывать из разных потоков без нарушения внутренних структур данных. В моем текущем подходе я использую мьютексы pthread для защиты всех доступов к переменным-членам. Это означает, что простая функция getter теперь блокирует и разблокирует мьютекс, и я беспокоюсь об этом, особенно потому, что API в основном будет использоваться в однопоточных приложениях, где любая блокировка мьютекса кажется чистой накладной.

Итак, я хотел бы спросить:

  • Есть ли у вас опыт работы с однопоточными приложениями, использующими блокировку по сравнению с теми, которые этого не делают?
  • насколько дороги эти вызовы блокировки/разблокировки, по сравнению, например. простой "return this- > isActive" для переменной-члена bool?
  • Вам известны лучшие способы защиты таких переменных доступов?
4b9b3361

Ответ 1

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

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

И, наконец, как уже отмечали другие, нет смысла защищать метод getter для чего-то вроде isActive с мьютексом - как только вызывающий получает возможность взглянуть на возвращаемое значение, значение, возможно, уже было изменено (поскольку мьютекс заблокирован только внутри метода геттера).

Ответ 2

"Мьютекс требует переключателя контекста ОС, что довольно дорого".

  • Это не относится к Linux, где мьютексы реализованы с использованием функции futex'es. Приобретение неопознанного (т.е. Еще не заблокированного) мьютекса, как указывает cmeerw, относится к нескольким простым инструкциям и обычно находится в области 25 наносекунд с текущим оборудованием.

Для получения дополнительной информации: Futex

Числа, которые должны знать все

Ответ 3

Это немного не по теме, но вы, кажется, новичок в потоковом - с одной стороны, только блокировка, где потоки могут перекрываться. Затем попытайтесь свести к минимуму эти места. Кроме того, вместо того, чтобы пытаться заблокировать каждый метод, подумайте о том, что поток выполняет (в целом) с объектом и делает это одним вызовом, и блокирует его. Постарайтесь максимально увеличить свои блокировки (это снова повышает эффективность и может/помочь/избежать блокировки). Но блокировки не "сочиняют", вы должны мысленно по крайней мере перекрестно организовать код, где потоки и перекрываются.

Ответ 4

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

Сначала я хотел бы получить это право (например, использовать блокировки), а затем беспокоиться о производительности. Я не знаю лучшего пути; что были созданы мьютексы.

Альтернативой для клиентов с одним потоком будет использование препроцессора для создания незаблокированной или заблокированной версии вашей библиотеки. Например:.

#ifdef BUILD_SINGLE_THREAD
    inline void lock () {}
    inline void unlock () {}
#else
    inline void lock () { doSomethingReal(); }
    inline void unlock () { doSomethingElseReal(); }
#endif

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

Ответ 5

Я могу сказать вам из Windows, что мьютекс является объектом ядра и, как таковой, несет (относительно) значительную блокировку накладных расходов. Чтобы получить более эффективную блокировку, когда все, что вам нужно, это тот, который работает в потоках, - это использовать критический раздел. Это не будет работать через процессы, а только потоки в одном процессе.

Однако.. linux - совсем другой зверь для многопроцессорной блокировки. Я знаю, что мьютекс реализован с использованием инструкций атомного процессора и применим только к процессу, поэтому они будут иметь такую ​​же производительность, что и критический раздел win32, т.е. Быть очень быстрым.

Конечно, самая быстрая блокировка вообще не нужна, или использовать их как можно меньше (но если ваша библиотека будет использоваться в сильно перевернутой среде, вам нужно будет блокироваться как можно скорее насколько это возможно: заблокировать, сделать что-то, разблокировать, сделать что-то еще, а затем заблокировать снова лучше, чем удерживать блокировку по всей задаче - стоимость блокировки не задерживается, но время, когда поток сидит вокруг twiddling его большие пальцы ждут другого потока, чтобы освободить блокировку, которую он хочет!)

Ответ 6

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

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

Знаете ли вы, как лучше защищать такие переменные обращения?

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

Ответ 7

Мне было интересно узнать об использовании pthred_mutex_lock/unlock. У меня был сценарий, где мне нужно было либо копировать где угодно от 1500-65 Кбайт без использования мьютекс или использовать мьютекс и сделать одну запись указателя на необходимые данные.

Я написал короткий цикл для проверки каждого

gettimeofday(&starttime, NULL)
COPY DATA
gettimeofday(&endtime, NULL)
timersub(&endtime, &starttime, &timediff)
print out timediff data

или

ettimeofday(&starttime, NULL)
pthread_mutex_lock(&mutex);
gettimeofday(&endtime, NULL)
pthread_mutex_unlock(&mutex);
timersub(&endtime, &starttime, &timediff)
print out timediff data

Если бы я копировал менее 4000 байтов, то операция прямой копии занимала меньше времени. Если, однако, я копировал более 4000 байт, тогда было бы дешевле делать блокировку/разблокировку мьютекса.

Время блокировки/разблокировки мьютекса длилось от 3 до 5 мкс, включая время для  gettimeofday для currentTime, который занял около 2 usec

Ответ 8

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

Во многих случаях вы можете использовать атомные встроенные функции, если ваш компилятор предоставляет их (если вы используете gcc или icc __sync_fetch *() и т.п.), но с ними трудно справиться правильно.

Если вы можете гарантировать, что доступ является атомарным (например, на x86 чтение или запись dword всегда является атомарным, если оно выровнено, но не read-modify-write), вы можете часто избегать блокировок вообще и использовать volatile вместо этого, но это не переносится и требует знания аппаратного обеспечения.

Ответ 9

Ну, субоптимальный, но простой подход заключается в размещении макросов вокруг ваших блокировок и разблокировок мьютексов. Затем используйте параметр компилятора /makefile для включения/отключения потоковой передачи.

Ex.

#ifdef THREAD_ENABLED
#define pthread_mutex_lock(x) ... //actual mutex call
#endif

#ifndef THREAD_ENABLED
#define pthread_mutex_lock(x) ... //do nothing
#endif

Затем при компиляции выполните a gcc -DTHREAD_ENABLED, чтобы включить потоки.

Снова я бы не использовал этот метод в любом крупном проекте. Но только если вы хотите что-то довольно простое.