LINUX.ORG.RU

Простая задачка с собеседований. О тредах.


0

1

Типа, какие операции нужно защитить от выполнения параллельно в нескольких потоках. Код:

if ( 10 == counter ) return;
   ++counter;

Насколько я понимаю, делать надо так:

mutex.lock();
if ( 10 == counter )
{
   mutex.unlock();
   return;
}
++counter;
mutex.unlock();

Нельзя ведь считать, что 10 == counter выполняется за 1 такт или ++counter за 1 такт. Кто знает что это за процессор и во что компилятор скомпилировал выражение «++counter;»? Вдруг запись выполняется извращенческим способом в 32 такта последовательным сдвигом 32-х бит слева направо, кто ж знает? Поэтому защищать надо и чтение и запись. Некоторые просто в этом примере утверждают, что чтение можно не защищать. И даже иногда бывают такие варианты ответов в тестах, которые не предполагают защиты чтения...

mutex.lock();
if ( 10 == counter )
{
   mutex.unlock();
   return;
}
++counter;
mutex.unlock();

в таком случае unlock будет выполняться 2 раза, так можно?

feinsbot
()
Ответ на: комментарий от feinsbot

с чего бы два? если 10, анлочим и выходи из ф-ции, если не 10, делаем полезность, анлочим, выходим. никогда оба анлока не выполнятся вместе

marvin_yorke ★★★
()
if ( 10 == counter ) return;
mutex.lock();
  ++counter;
mutex.unlock();

так правильно. 10 == counter не меняет значение counter, соответственно смысла в блокировании нет никакого.

invy ★★★★★
()
Ответ на: комментарий от invy

ещё есть в атомарные функции инкремента, например в winapi - InterlockedIncrement - оня значительно быстрее мутекса.

Так что даже так

if ( 10 == counter ) return;
InterlockedIncrement(counter);

invy ★★★★★
()

чтение нужно защищать. Если хочешь повысить производительность - используй cas-операции

maxcom ★★★★★
()

Если лочить, то
1. место перед проверкой на 10
2. место, инкрементирующее переменную в другом потоке.

yoghurt ★★★★★
()
Ответ на: комментарий от invy

так правильно. 10 == counter не меняет значение counter, соответственно смысла в блокировании нет никакого.

смысл есть в том, что данную проверку могут пройти несколько потоков, и каждый потом проинкрементит counter

aho
()

вообще от контекста зависит, от логики программы, от того, чем занимаются другие потоки.

Если нельзя допускать, чтобы значение counter превысило 10, тогда надо блокировать все. Если пофиг - тогда вообще можно вместо мютекса использовать функцию атомарного инкремента

Harald ★★★★★
()

mutex.unlock();

И, да, раз уж на то пошло, используй scoped-локи - они ексепшн-сейф и в какой-то мере дедлок-сейф.

yoghurt ★★★★★
()
Ответ на: комментарий от Harald

Что даст атомарный инкремент, если надо предварительно проверять значение?

Функции вида atomic_test_and_inc я не нашёл ни в gcc, не в винапи

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

Чем такой код:

mutex.lock();
if(10 == counter)
{
 mutex.unlock()
 return;
}
отличается от
if(10 == counter)
{
 return;
}
Если проверка пройдена, нас больше ничего не волнует - мы делаем return. Что было с counter до проверки или станет после нас не волнует.

invy ★★★★★
()
Ответ на: комментарий от invy

Что было с counter до проверки или станет после нас не волнует.

еще как волнует, в данном случае имеет место цикл, например, от 0 до 9, если после проверки counter проинкрементится в двух местах при старом значении counter == 9, то цикл не прекратится на 10 и пойдет дальше

aho
()
Ответ на: комментарий от aho

Хоть защищай хоть не защищай counter станет равным 11. Например: 11 тредов, все по очереди прошли проверку (с блокированием чтения) counter всё ещё равен нулю. После чего все 11 тредов последовательно (с блокированием) инкрементируют counter.

Если в таких случах люди хотят написать цикл, то проверка должна иметь вид if(counter >= 10) return;

Суть защиты чтения, только, в случае извращённой записи (и незапланированного принимания переменной counter значения 10). Тут с ТС можно согласиться и то в данном случае это только потеря производительности.

invy ★★★★★
()
Ответ на: комментарий от invy

Например: 11 тредов, все по очереди прошли проверку (с блокированием чтения) counter всё ещё равен нулю

Бгг. Все по очереди проверку они не пройдут, если чтение блокировать. Учи матчасть.

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

Ну так и что? После cmp, бит установлен. Дальше условный jmp не зависит от значения переменной counter, а только от значения бита.

invy ★★★★★
()
Ответ на: комментарий от invy

Представь, что counter = 9. Ты проверил его, понял, что оно не 10, что можно инкрементировать. Это была одна инструкция, cmp.

Тут у процесса вышел квант, контекст переключился, второй поток сравнил 9 с 10 и невозбранно её инкрементировал.

Потом контекст выполнения переключился обратно на наш поток, который уснул после cmp, пробудился, инкрементировал не глядя и получили 11.

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

Почему же не пройдут? Это смотря как писать. Если я отдельно блокирую if(counter == 10) и отдельно counter++ Например

mutex.lock();
 test = (counter == 10);
mutex.unlock()

if(test) return;

mutex.lock();
 counter++;
mutex.unlock();

invy ★★★★★
()
Ответ на: комментарий от invy
mutex.lock();
 test = (counter == 10);
mutex.unlock()

if(test) return;

Либо ты издеваешься, либо троллишь, либо действительно не понимаешь, что так пишут только ССЗБ.

yoghurt ★★★★★
()
Ответ на: комментарий от ckotinko

а, не, вру

1:mov eax, [counter]
lea edx, [eax+1]
cmp eax, 10
je __return__
lock cmpxchg [counter], edx
jne 1b

считал, проверил, вышел если 10. иначе прибавил 1, убедился что никто не обосрал память, записал, вышел, иначе заново

ckotinko ☆☆☆
()
Ответ на: комментарий от yoghurt

на Java как-то так:

for (;;) {
  int current = counter.get();
  int next = current + 1;
  if (next>10) {
    return; // кто-то уже насчитал 10, выходим
  }

  if (counter.compareAndSet(current, next)) {
   if (next!=10) {
    break; // 10 еще нет, выходим из цикла чтобы сделать что-то
   } else {
    return; // 10 достигли, выходим из функции
   }
  }
}
maxcom ★★★★★
()
Ответ на: комментарий от ckotinko

на асме надо еще внимательно подумать про модель памяти процессора, возможно надо еще memory barrier вставить перед чтением из counter'а

maxcom ★★★★★
()
Ответ на: комментарий от yoghurt

В условии чётко не сказано в чём тайный смысл такого кода. Так что троллит наверное нас ТС :)
А если между if(counter == 10) return;
и counter++;
идут какие-нибудь операции, занимающие много времени, то именно так и делают... иначе получится собственно-написанный 12309 :)

invy ★★★★★
()
Ответ на: комментарий от maxcom

На джава synchronized(counter) { if(counter == 10) return; ++counter; } Или в зависимости от логики отдельно два раза synchronized() ...

invy ★★★★★
()
Ответ на: комментарий от invy

synchronized для такой задачи избыточен, слишком большой overhead

maxcom ★★★★★
()
Ответ на: комментарий от invy

это не говоря уже про то, что synchronized(counter) не скомпилится (если counter типа int), либо не будет корректно работать (если counter типа Integer)

maxcom ★★★★★
()
Ответ на: комментарий от maxcom

а ну да, только же с объектами работает...

invy ★★★★★
()
Ответ на: комментарий от invy

на некоторых smp он и при чтении может упасть.

sergej ★★★★★
()

Вопрос некорректен

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

world
()
Ответ на: комментарий от hidden_4003

Так пишут чтобы в случае опечатки = вместо == выражение не скомпилировалось и выдало ошибку.

gcc в таком случаях предлагает:

«error: suggest parentheses around assignment used as truth value»

aho
()

А если способ с двойной проверкой? В случае достижения граничного значения блокировка не срабатывает.

if (counter == 10) {
  mutex.lock();
  if (counter != 10) {
    ++ counter;
  }
  mutex.unlock();
}
dave ★★★★★
()
Ответ на: комментарий от dave
if (counter != 10) {
  mutex.lock();
  if (counter != 10) {
    ++ counter;
  }
  mutex.unlock();
}
dave ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.