2013-05-07 1 views
13

вопрос от работы-интервьюПараллельное инкрементацию из Int переменной

int count = 0; 

void func1() 
{ 
    for (int i =0 ; i < 10; ++i) 
    count = count + 1; 
} 

void func2() 
{ 
    for (int i =0 ; i < 10; ++i) 
    count++; 
} 

void func3() 
{ 
    for (int i =0 ; i < 10; ++i) 
    ++count; 
} 

int main() 
{ 
    thread(func1); 
    thread(func2); 
    thread(func3); 

    //joining all the threads 

    return 0; 
} 

Вопрос заключается в том: что диапазон значений count может принять теоретически возможный? Верхняя граница, по-видимому, равна 30, но какая нижняя? Мне сказали, что это 10, но я не уверен в этом. В противном случае, зачем нам нужны барьеры памяти?

Итак, какова нижняя граница диапазона?

+4

расы данных являются UB, как таковой любой диапазон «действует». – PlasmaHH

+2

Если 'int count' был заменен на' std :: atomic count', то (я считаю), что мы больше не имеем неопределенного поведения. Предварительное приращение и пост-приращение являются атомарными; но 'count = count + 1' не гарантированно будет атомарным - это вернет нас к UB или просто означает, что у нас есть неопределенное поведение? –

+1

Это только неуказано, и в этом случае вы действительно можете получить возможный диапазон значений. –

ответ

13

Ответ Джеймса Канзе является правильным для всех практических целей, но в данном конкретном случае, если код точно так же написан, а используемый здесь thread - это std::thread из C++ 11, поведение фактически определено.

В частности, thread(func1); запустится нить. func1. Затем, в конце выражения, объект временной нити будет уничтожен, без соединения или отсоединения. Таким образом, поток все еще соединяется, и стандарт определяет, что в таком случае деструктор вызывает std::terminate. (См. [Thread.thread.destr]: «Если joinable(), то завершайте(), иначе никаких эффектов.») Таким образом, ваша программа прерывается.

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

+0

Это приветственное уведомление, спасибо! – fogbit

+0

Ссылки на стандарт приветствуются. (очень хорошая точка, в противном случае) – qdii

+0

@qdii Добавлена ​​стандартная цитата. –

15

Это неопределенное поведение, поэтому count может принимать любые значения imaginable. Или программа может потерпеть крах.

+0

Что делать, если 'int' - это одно слово? –

+0

Я имею в виду, не означает ли это, что операция атомарна? (исключая фактически 'func1') –

+0

Это не атомный стандарт. – avakar

4

Начиная с простой части, очевидная верхняя граница равна 30, поскольку, если все идет правильно, у вас есть 3 вызова функций; каждый из которых способен увеличивать count 10 раз. В целом: 3 * 10 = 30.

Объявление на нижней границе, они правильные, и именно поэтому сценарий наихудшего сценария состоит в том, что каждый раз, когда один поток пытается увеличить count, другие потоки будут делать это точно в одно и то же время. Имея в виду, что ++count на самом деле является следующий псевдокод:

count_temp = count; 
count_temp = count_temp+1; 
count = count_temp; 

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

+6

-1. Неопределенное поведение не определено. –

+0

Неопределенное поведение не определено. Однако это хорошо определенное поведение, так как диапазон возможных результатов хорошо определен. Счет идет только вверх. – levengli

+0

Потому что это неправильно. Параллельная модификация - это неопределенное поведение, и никакая аргументация не даст вам верхней или нижней границы возможных значений count. –

1

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

Стандартные состояния довольно четко в разделе 1,10 пункте 21: The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.

Однако термин undefined behavior также определен в стандарте, раздел 1.3.24: behavior for which this International Standard imposes no requirements... Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment...

Принимая ответ Sebasian в отношении std::terminate на счет, и работая в предположении, что эти потоки не будут вызывать исключение, тем самым вызывая преждевременное прекращение; в то время как стандарт не определяет результат - довольно очевидно, что это может быть из-за простоты алгоритма. Другими словами, хотя 100% точный ответ будет заключаться в том, что результат не определен, я все же утверждаю, что диапазон возможных результатов хорошо определен и составляет 10-30 из-за characteristic of the environment.

BTW - Я действительно хотел сделать это замечание вместо другого ответа, однако он был слишком длинным

+0

Для вашей защиты я говорю, что ответ, который вы дали, - это именно тот, который хотят получить парни :-) – fogbit