2010-01-28 3 views
2

У меня есть переменная счетчика, к которой будут доступны несколько потоков, которые будут увеличивать/уменьшать ее. Он не должен обновляться несколькими потоками одновременно.Проблема многопоточности C++ - это mutex единственный способ?

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

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

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

спасибо.

+0

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

+0

re Java: нет, 'synchronized' в java эквивалентно критическому разделу, который отличается от' volatile' и атомного приращения/уменьшения. – finnw

ответ

8

Большинство процессоров имеют команды «atomic» - инструкции по увеличению и уменьшению - в значительной степени они реализуются на уровне машины на уровне мьютексов.

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

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

См Atomic Instruction

3

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

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

+0

Итак, вы ничего не можете поставить перед объявлением переменной, которое указывает, что к нему нужно получить доступ атомарным способом? Я знаю, что const, mutable и volatile являются ключевыми словами C++, но они не соответствуют счету. – Andy

+0

@ Энди Нет, нет. Текущий стандарт C++ не затрагивает потоковую передачу вообще. – 2010-01-28 13:03:01

+0

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

0

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

+0

'sig_atomic_t' имеет атомную запись (т. Е. Вам не нужно беспокоиться только о 3 байтах переменной, обновляемой до контекстного переключателя), но не атомном приращении/уменьшении. См. Этот вопрос: http://stackoverflow.com/questions/1762148/atomic-instruction – finnw

2

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

PS от here, соответствующий 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); 
} 
4

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

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

и отсканировать его от других функций.

+1

В библиотеке qt есть хорошая реализация атомных операций http://doc.trolltech.com/4.3/atomic-operations.html – Jay

3

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

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

0

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

+0

Спасибо , У C++ есть что-то, чтобы сделать переменную local в потоке - declspec (thread), но она, вероятно, специфична для платформы Windows. – Andy

+0

@Andy: __declspec (thread) делает переменные нито-аффинными - это означает, что они не могут быть разделены между потоками (ну, по крайней мере, не нарушая их цели). – rjnilsson

+0

@ Andy: О, и __declspec (thread) не зависит от Windows, это VC++. – rjnilsson

0

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

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

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