Я пытаюсь понять, как правильно разрабатывать многопоточные приложения.
В текущем проекте у меня есть следующий класс:
class Test
{
public:
void setVal(unsigned int val)
{
mtx.lock();
testValue = val;
mtx.unlock();
}
unsigned int getVal()
{
unsigned int copy = testValue;
return copy;
}
private:
boost::mutex mtx;
unsigned int testValue;
}
И мой вопрос: выше ли метод Test :: getVal () безопасен в многопоточной среде, или он должен быть заблокирован перед тем, как делать копию?
Я прочитал несколько статей о COW, и теперь я не уверен.
Спасибо!
Если у вас есть данные, которые могут быть разделены между несколькими потоками (например, testValue
член в вашем случае), вы должен синхронизировать все обращения к этим данным. «Синхронизировать» здесь имеет широкое значение: это можно сделать с помощью мьютекса, делая данные атомарными или явно вызывая барьер памяти.
Но вы не можете пропустить это. В параллельном мире с несколькими потоками, ядрами ЦП, ЦП и кэшами нет никакой гарантии, что запись одним потоком будет видна другому потоку, если они не «пожмут друг другу руки» примитива синхронизации. Вполне возможно, что запись кэша потока T1 для testValue
не будет обновляться, когда поток T2 записывает в testValue
Именно потому, что система управления кэшем HW видит, что «синхронизация не происходит, потоки не получают доступ к общим данным, почему я должен торпедировать производительность путем аннулирования кэшей?»
В стандартной главе C ++ 11 [intro.multithread] содержится более подробная информация, чем вы хотели бы, но вот неофициальное примечание из этой главы, обобщающее эту идею:
5 … Неофициально, выполняя операцию освобождения заставляет предшествующую сторону
эффекты в других областях памяти, чтобы стать видимыми для других потоков, которые позже выполняют потребление или
приобрести операцию на A. …