синхронизация потоков linux - PullRequest
5 голосов
/ 07 апреля 2010

Я новичок в Linux и Linux темы. Я потратил некоторое время на поиск в Google, чтобы понять разницу между всеми функциями, доступными для синхронизации потоков. У меня все еще есть вопросы.

Я обнаружил все эти различные типы синхронизации, каждая из которых имеет ряд функций для блокировки, разблокировки, тестирования блокировки и т. Д.

  • gcc атомарные операции
  • futexes
  • мьютексы
  • 1012 * Взаимные блокировки *
  • seqlocks
  • rculocks
  • условия
  • семафоров

Мое нынешнее (но, вероятно, ошибочное) понимание таково:

Семафоры имеют широкий процесс, включают файловую систему (я полагаю, что это практически так) и, вероятно, являются самыми медленными.

Futexes может быть базовым механизмом блокировки, используемым мьютексами, спин-блокировками, seqlocks и rculocks. Futexes может быть быстрее, чем механизмы блокировки на их основе.

Спинлоки не блокируют и, таким образом, избегают скачков контекста. Однако они избегают переключения контекста за счет использования всех циклов ЦП до снятия блокировки (вращения). Их следует использовать только в многопроцессорных системах по понятным причинам. Никогда не спи в спинлок.

Блокировка seq просто сообщает вам, когда вы закончили работу, если писатель изменил данные, на которых была основана работа. Вы должны вернуться и повторить работу в этом случае.

Атомарные операции являются самым быстрым синхронизирующим вызовом и, вероятно, используются во всех вышеупомянутых механизмах блокировки. Вы не хотите использовать атомарные операции над всеми полями в ваших общих данных. Вы хотите использовать блокировку (mutex, futex, spin, seq, rcu) или одну атомарную операцию над флагом блокировки при доступе к нескольким полям данных.

Мои вопросы звучат так:

  1. Прав ли я до сих пор со своими предположениями?

  2. Кто-нибудь знает стоимость цикла процессора различных опций? Я добавляю параллелизм к приложению, чтобы мы могли лучше реагировать на настенное время за счет запуска меньшего количества экземпляров приложения на ящик. Представления - самое большое внимание. Я не хочу использовать процессор с переключением контекста, вращением или множеством дополнительных циклов процессора для чтения и записи общей памяти. Я абсолютно обеспокоен количеством циклов процессора.

  3. Какие (если таковые имеются) блокировки предотвращают прерывание потока планировщиком или прерыванием ... или я просто идиот, и все механизмы синхронизации делают это. Какие виды прерывания предотвращены? Могу ли я заблокировать все потоки или потоки только на процессоре блокирующего потока? Этот вопрос связан с моим страхом прерывания потока, удерживающего блокировку для очень часто используемой функции. Я ожидаю, что планировщик может запланировать любое количество других работников, которые, вероятно, столкнутся с этой функцией и затем заблокируют, потому что она была заблокирована. Большая часть переключения контекста будет потрачена впустую, пока поток с блокировкой не будет перенесен и завершен. Я могу переписать эту функцию, чтобы минимизировать время блокировки, но все же она так часто называется, что я хотел бы использовать блокировку, которая предотвращает прерывание ... на всех процессорах.

  4. Я пишу код пользователя ... поэтому я получаю программные прерывания, а не аппаратные ... верно? Я должен держаться подальше от любых функций (блокировки spin / seq), в которых есть слово "irq".

  5. Какие блокировки предназначены для написания кода ядра или драйвера, а какие предназначены для пользовательского режима?

  6. Кто-нибудь думает, что использование атомарной операции для перемещения нескольких потоков по связанному списку - чокнутый? Я думаю атомарно изменить указатель текущего элемента на следующий элемент в списке. Если попытка сработает, то поток может безопасно использовать данные, на которые указывал текущий элемент, прежде чем он был перемещен. Другие темы теперь будут перемещаться по списку.

  7. futexes? Есть ли основания использовать их вместо мьютексов?

  8. Есть ли лучший способ, чем использовать условие для сна потока, когда нет работы?

  9. При использовании атомарных операций gcc, в частности test_and_set, могу ли я повысить производительность, выполнив сначала неатомарный тест, а затем используя test_and_set для подтверждения? Я знаю, что это будет конкретный случай, так что вот случай. Существует большая коллекция рабочих предметов, скажем, тысячи. Каждый рабочий элемент имеет флаг, который инициализируется равным 0. Когда поток имеет эксклюзивный доступ к рабочему элементу, этот флаг будет равен одному. Там будет много рабочих потоков. Каждый раз, когда поток ищет работу, он не может атомарно проверить на 1. Если они читают 1, мы точно знаем, что работа недоступна. Если они читают ноль, им нужно выполнить атомарный test_and_set для подтверждения. Так что, если атомарный test_and_set равен 500 циклам процессора, потому что он отключает конвейерную обработку, заставляет процессоры обмениваться данными и кэши L2 сбрасываются / заполняются .... и простой тест - 1 цикл .... тогда, пока у меня было лучшее соотношение от 500 до 1, когда дело дошло до того, что мы наткнулись на уже выполненные рабочие задания ... это будет победа.

Я надеюсь использовать мьютексы или спин-блокировки, чтобы спарлиново защитить разделы кода, которые я хочу, чтобы только один поток в СИСТЕМЕ (не только ЦП) имел доступ одновременно. Я надеюсь экономно использовать gcc atomic ops для выбора работы и минимизации использования мьютексов и спин-блокировок. Например: флаг в рабочем элементе может быть проверен, чтобы увидеть, работал ли поток с ним (0 = нет, 1 = да или в процессе). Простой test_and_set сообщает потоку, работает ли он или должен двигаться дальше. Я надеюсь использовать условия для пробуждения потоков, когда есть работа.

Спасибо!

Ответы [ 5 ]

2 голосов
/ 08 января 2011

Спасибо всем, кто ответил. Мы прибегли к использованию атомарных операций gcc для синхронизации всех наших потоков. Атомные операции были примерно в 2 раза медленнее, чем установка значения без синхронизации, но на величины быстрее, чем блокировка мьютекса, изменение значения, а затем разблокировка мьютекса (это становится очень медленным, когда у вас начинают стучать потоки в блокировки ...) используйте только pthread_create, attr, cancel и kill. Мы используем pthread_kill, чтобы сигнализировать потокам о пробуждении, которое мы усыпляем. Этот метод в 40 раз быстрее, чем cond_wait. Так что в основном .... используйте pthreads_mutexes, если у вас есть время тратить .

2 голосов
/ 07 апреля 2010

Код приложения, вероятно, должен использовать функции потока posix. Я полагаю, у вас есть справочные страницы, наберите

man pthread_mutex_init
man pthread_rwlock_init
man pthread_spin_init

Ознакомьтесь с ними и с функциями, которые с ними работают, чтобы выяснить, что вам нужно.

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

0 голосов
/ 08 апреля 2010

Замечание о фьютексах - их более наглядно называют быстрые мьютексы пространства пользователя . С помощью futex ядро ​​включается только тогда, когда требуется арбитраж, что обеспечивает ускорение и экономию.

Реализация futex может быть чрезвычайно сложной (PDF), их отладка может привести к безумию. Если вам действительно очень нужна скорость, лучше всего использовать реализацию pthread mutex.

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

0 голосов
/ 07 апреля 2010

в дополнение вы должны проверить следующие книги

  • Программирование Pthreads: POSIX Стандарт для лучшей многопроцессорной обработки

и

  • Программирование с потоками POSIX (R)
0 голосов
/ 07 апреля 2010

по вопросу № 8 Есть ли лучший способ, чем использовать условие для сна потока, когда нет работы? да, я думаю, что лучший подход вместо использования сна использует такие функции, как sem_post () и sem_wait из "semaphore.h"

привет

...