Проблема многопоточности в C ++ - является ли мьютекс единственным способом? - PullRequest
2 голосов
/ 28 января 2010

У меня есть переменная счетчика, к которой будут обращаться несколько потоков, которые будут увеличивать / уменьшать ее. Он не должен обновляться несколькими потоками одновременно.

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

Есть ли другой способ сделать это без использования мьютекса? Использование мьютекса действительно снижает производительность (см. http://www.codeguru.com/forum/showthread.php?t=333192).. Я считаю, что в Java есть ключевое слово, которое можно использовать в объявлении переменной для достижения этой цели (называется ли оно «синхронизировано»?), Но есть такое вообще в С ++?

Я знаю, что volatile - это не ключевое слово, которое я ищу.

Большое спасибо.

Ответы [ 8 ]

8 голосов
/ 28 января 2010

У большинства процессоров есть инструкции atomic 'для увеличения и уменьшения - в значительной степени это то, как мьютексы реализованы на уровне машины.

Вы можете получить доступ к этим элементарным инструкциям в своем собственном коде. Windows предоставляет функцию InterlockedIncrement(), а glib предоставляет эквиваленты . В языке ассемблера x86 вы можете использовать LOCK CMPXCHG и kin напрямую.

C ++ ничего не знает об этих понятиях - вы должны использовать их самостоятельно; В C ++ нет волшебных ключевых слов для безопасности потоков.

См. Атомная инструкция

4 голосов
/ 28 января 2010

Хотя использование атомарных операций, вероятно, является наиболее эффективным, тот факт, что он используется более чем в одной функции, не является препятствием для использования критической секции в этом или любом другом коде - просто напишите функцию:

void IncDec( bool inc ) {
   EnterCritical Section( theCS );
   if ( inc ) {
     theVar++;
   }
   else {
     thevar--;
   }
   LeaveCriticalSection( theCS );
}

и вызывайте его из других ваших функций.

3 голосов
/ 28 января 2010

Критическая секция в этом случае не подходит, потому что существует более 1 функции, которая может изменить рассматриваемую переменную.

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

3 голосов
/ 28 января 2010

Из описания звучит так, что, возможно, вам нужен только InterlockedIncrement и связанная функция декремента.

Редактировать - это функции Windows ... Я не переставал спрашивать, какая платформа.

2 голосов
/ 28 января 2010

В Win32 IntelockedIncrement / IntelockedIncrement64 и связанные с ним операции компилируются в инструкции x86, которые допускают атомарные операции на уровне процессора для 32- или 64-разрядных слов (в зависимости от вашей архитектуры). Это хорошо работает в случае простого счетчика, но, естественно, не будет работать, если вы пытаетесь синхронизировать большую структуру с несколькими словами.

PS из здесь , соответствующий asm, который вам понадобится для реализации в системе без Win32, работающей на x86.

inline long InterlockedExchangeAdd( long* Addend, long Increment )
{
long ret;
__asm__ (
/* lock for SMP systems */
"lock\n\t"
"xaddl %0,(%1)"
:"=r" (ret)
:"r" (Addend), "0" (Increment)
:"memory" );
return ret;
}

inline long InterlockedIncrement( long* Addend )
{
return InterlockedExchangeAdd( Addend, 1 );
}

inline long InterlockedDecrement( long* Addend )
{
return InterlockedExchangeAdd( Addend, -1 );
}
0 голосов
/ 28 января 2010

Критическая секция в этом случае не подходит, потому что существует более 1 функции, которая может изменить указанную переменную.

Как вы узнаете, что нашли все места, к которым может обращаться переменная, если вы не создадите ни одной функции, использующей критический раздел для ее изменения?

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

0 голосов
/ 28 января 2010

Как уже упоминалось, в текущем стандарте C ++ нет ничего, что поддерживало бы атомарный доступ к переменным. Однако, если вы запрашиваете поддержку библиотеки C ++ (мне это не совсем понятно), есть попытка имитировать атомарную поддержку грядущего стандарта C ++ здесь .

0 голосов
/ 28 января 2010

Вы можете использовать атомарный тип для переменной счетчика - например, sig_atomic_t (в GNU libc). Тогда нет необходимости в синхронизации, так как не может возникнуть условие гонки, операция с этой переменной гарантированно будет атомарной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...