В моем игровом движке C ++ у меня есть система заданий, которая использует рабочие потоки для выполнения различных задач. Потоки привязываются к каждому доступному ядру. Недавно я пытался оптимизировать некоторые из моих системных конвейеров, максимально увеличив загрузку процессора. Вот пример псевдо-i sh кода. Это не точная копия, но ситуация похожа.
struct entityState {
uint8 * byteBuffer; // Serialized binary data for the Entity
uint8 * compressedData; // Compressed version of Entity data
uint64 guid; // Unique ID
gameTimeMS lastUpdated; // last time buffer was updated in milliseconds
uint32 numUpdates; // Count of the number of updates
uint32 numTimesAckedOverNetwork; // How many times client acked the data
const char * typeData; // Type data in place of RTT
bool markedForDelete; // Whether this object should be deleted next frame
const char * debugData; // In debug configs, store meta data
// More member data but the point is made
};
// For examples sake, I have a contiguous array of entityState data
List< entityState * > entityStateList;
PopulateListWithEntityStateData(); // ~20,000 entityState ptrs on average
SortEntityStateList();
// Fire off 5 jobs each with their own worker thread
StartEntityStateJobs();
У меня есть 5 заданий, которые работают в этом списке одновременно без мьютексов или критических секций . Каждая функция задания получает доступ к массиву посредством бинарного поиска на основе критериев, таких как guid, или просто линейного поиска. Здесь подвох. Ни одна из функций задания не изменяет одни и те же данные-члены объектов entityState в entityStateList . Тем не менее, они могут задерживать один и тот же entityState ptr из-за бинарного поиска против линейного поиска, имеющего коллизии. Но, повторяю, они никогда не изменяют одни и те же данные участника одновременно. Никакие ptrs данных членов не разыменовываются одновременно в каждом потоке.
Я запустил эту симуляцию с модульным тестом и не обнаружил никаких проблем. Тем не менее, у меня есть друзья-программисты, которые говорят, что существует очень и очень малая вероятность того, что это приведет к неопределенному поведению с остановкой и возобновлением потоков при разыменовании одного и того же entityStatePtr.
Другой момент, о котором я слышал, заключается в том, что причина, по которой эта настройка сработала, состоит в том, что размер структуры entityState не помещается в строку кэша и в итоге разделяет выборку данных, которая сама по себе действует как данные. Сама защита из-за разделения данных структуры на разные строки кэша. Для пояснения, скажем, верхняя половина помещается в одну строку кэша, а нижняя - в другую, а функции задания работают только с одним членом данных entityState ptr, и большую часть времени это происходит в другой строке кэша. Я не использую никакие модификаторы или операции Atomi c для данных члена, потому что ни одно задание не касается данных члена.
Наконец, у меня также есть несколько друзей-программистов, которые говорят, что это совершенно безопасно для потоков.
Тем не менее, у меня есть три различных утверждения, и мои знания низкого уровня не достаточны для многопоточности, чтобы определить, какие это утверждение правильно.
Вопрос в том ... возможно ли сверхнизкое cra * sh, которое может произойти в дикой природе 1 раз из 'x'? Даже 1/1 миллион не приемлемо. Это безопасный механизм потоков lockless для параллельного выполнения нескольких операций над списком? Попробуйте не обращать внимания на тривиальность данных примера. Это намного сложнее в моем примере двигателя. Этот код может работать на нескольких ОС, таких как P C, Linux и консоли. Это еще не до sh, но воздействие и испытания ограничены. Я признаю, что я не эксперт низкого уровня, но это экономит драгоценное время работы. Итак, я жду, чтобы наткнуться на мину или это безопасно? Компилятор g cc версия C ++ 11. Также, пожалуйста, избегайте производительности topi c, если это не связано с многопоточностью и / или безопасностью потоков. Я знаю, что промах кэша плох.
Вопрос - Является ли потокобезопасным или нет? Если да или нет, пожалуйста, объясните, почему, если возможно, подробно. Я хотел бы поддержать свои знания низкого уровня.