Этот код потокобезопасен? - PullRequest
1 голос
/ 01 декабря 2010

Допустим, у нас есть поточно-ориентированная функция сравнения и замены, такая как
long CAS(long * Dest ,long Val ,long Cmp)
который сравнивает Dest и Cmp, копирует Val в Dest, если сравнение прошло успешно, и возвращает исходное значение Dest атомарно.

Поэтому я хотел бы спросить вас, является ли приведенный ниже код поточно-ориентированным.

while(true)
{
    long dummy = *DestVar;
    if(dummy == CAS(DestVar,Value,dummy) )
     {
        break;
    }

}

EDIT:
Параметры Dest и Val - это указатели на переменные, созданные в куче. InterlockedCompareExchange - пример использования функции CAS.

Ответы [ 3 ]

2 голосов
/ 01 декабря 2010

Изменить.Редактирование вопроса означает, что большая часть этого не имеет значения.Тем не менее, я оставлю это, поскольку все проблемы в случае C # также относятся к случаю C ++, но случай C ++ приносит намного больше проблем, как указано, так что это не совсем не имеет значения.


Да,но ...

Предполагая, что вы имеете в виду, что этот CAS является атомарным (что имеет место в C # Interlocked.CompareExchange и с некоторыми вещами, доступными для использования в некоторых библиотеках C ++), он сам по себе потокобезопасен.

Однако DestVar = Value может быть поточно-безопасным и сам по себе (это будет в C #, независимо от того, находится он в C ++ или нет, зависит от реализации).

В C # запись вцелое число гарантированно будет атомарным.Таким образом, выполнение DestVar = Value не приведет к ошибке из-за того, что что-то происходит в другом потоке.Это «потокобезопасный».

В C ++ таких гарантий нет, но есть некоторые процессоры (на самом деле, давайте сейчас просто отбросим C ++, достаточно сложность, когда речь идет о более сильных гарантиях C #).и C ++ имеет все эти сложности и даже больше, когда речь идет о подобных проблемах).

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

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

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

Для сравнения посмотрите на ответ на вопрос Является ли использование статической очереди потокобезопасным? и объяснение того, как он работает.Обратите внимание, что в каждом случае CAS допускается сбой, потому что его сбой означает, что другой поток сделал что-то «полезное», или когда он проверен на успех, выполняется больше, чем просто остановка цикла.Это комбинации CAS, каждый из которых обращает внимание на возможное состояние, вызванное другими операциями, которые позволяют безблокировочный код без ожидания, который является потокобезопасным.

И теперь мы закончили с этим, отметим также, чтоВы не можете перенести это непосредственно на C ++ (сбор мусора зависит от того, чтобы некоторые возможные сценарии ABA не имели большого значения, с C ++ бывают ситуации, когда могут возникнуть утечки памяти).Это также имеет значение, на каком языке вы говорите.

1 голос
/ 01 декабря 2010

Невозможно сказать, для любой среды. Вы не определяете следующее:

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

  • Если DestVar и Value находятся в куче, то являются ли они ссылочными типами или типами значений (имеют копирование по семантике присваивания). Если последнее, то это потокобезопасно.

  • Синхронизирует ли CAS доступ к себе? Другими словами, имеет ли он некую структуру взаимного исключения, которая допускает только один вызов за раз? Если это так, то это потокобезопасно.

  • Если любое из условий, упомянутых выше, не соответствует действительности, то неопределимо независимо от того, все ли это потокобезопасно. С более подробной информацией об упомянутых выше условиях (а также о том, является ли это C ++ или C #, да, это имеет значение ), можно получить ответ.

0 голосов
/ 02 декабря 2010

На самом деле, этот код неисправен.Либо вам нужно знать, как компилятор читает *DestVar (до или после CAS), который имеет сильно отличную семантику, либо вы пытаетесь включить *DestVar, пока какой-то другой поток не изменит его.Это, конечно, не первый, так как это было бы сумасшествием.Если это последнее, то вы должны использовать свой оригинальный код.В настоящее время ваша ревизия не является поточно-ориентированной, поскольку вообще не безопасна.

...