Существует множество методов, которые можно использовать для работы с многопоточностью, если вы разрабатываете проект для него.
Самым общим и универсальным является просто «избегать общего состояния». По возможности копируйте ресурсы между потоками, а не заставляйте их обращаться к одной и той же общей копии.
Если вы пишете низкоуровневый код синхронизации самостоятельно, вы должны помнить, чтобы не делать абсолютно никаких предположений. И компилятор, и процессор могут переупорядочить ваш код, создавая условия гонки или взаимоблокировки, когда при чтении кода это не представляется возможным. Единственный способ предотвратить это - с помощью барьеров памяти. И помните, что даже самая простая операция может быть связана с проблемами потоков. Такие простые вещи, как ++i
, обычно не являются атомарными, и если несколько потоков обращаются к i
, вы получите непредсказуемые результаты.
И, конечно, только потому, что вы присвоили значение переменной, это не гарантирует, что новое значение будет видно другим потокам. Компилятор может отложить запись в память. Опять же, барьер памяти заставляет его «сбрасывать» все ожидающие операции ввода-вывода памяти.
На вашем месте я бы использовал модель синхронизации более высокого уровня, чем простые блокировки / мьютексы / мониторы / критические секции, если это возможно. Существует несколько библиотек CSP , доступных для большинства языков и платформ, включая языки .NET и нативный C ++.
Это обычно делает условия гонки и взаимоблокировки тривиальными для обнаружения и исправления, и допускает нелепый уровень масштабируемости. Но с этой парадигмой также связано определенное количество накладных расходов, поэтому каждый поток может выполнить меньше работы, чем с другими методами. Это также требует, чтобы все приложение было структурировано специально для этой парадигмы (поэтому сложно перестроить существующий код, но поскольку вы начинаете с нуля, это не проблема, но она все равно будет вам незнакома)
Другим подходом может быть Транзакционная память . Это легче вписать в традиционную структуру программы, но также имеет некоторые ограничения, и я не знаю многих библиотек производственного качества для этого (недавно был выпущен STM.NET, и, возможно, стоит проверить. Intel имеет C ++ компилятор со встроенными в язык расширениями STM)
Но какой бы подход вы ни использовали, вам придется тщательно подумать о том, как разделить работу на независимые задачи и как избежать перекрестных разговоров между потоками. Каждый раз, когда два потока обращаются к одной и той же переменной, у вас есть потенциальная ошибка. И всякий раз, когда два потока обращаются к одной и той же переменной или просто к другой переменной рядом с тем же адресом (например, следующий или предыдущий элемент в массиве) , данные должны будут обмениваться между ядрами, заставляя их сбрасывается из кэша процессора в память, а затем считывается в кэш другого ядра. Что может быть главным ударом по производительности.
Да, и , если вы пишете приложение на C ++, не стоит недооценивать язык. Вам нужно будет подробно выучить язык, прежде чем вы сможете писать надежный код, гораздо менее надежный многопоточный код.