Атомное решение. Это очень быстрый способ получить память для ваших аргументов.
Если аргументы всегда имеют одинаковый размер, предварительно выделите их несколько. Добавьте pNext в вашу структуру, чтобы связать их вместе. Создайте глобальный _pRecycle для хранения всех доступных в виде связанного списка, используя pNext для их связывания. Когда вам нужен аргумент, используйте атомарные операции для CAS один из заголовка списка мусора. Когда вы закончите, используйте атомарные операции, чтобы вернуть аргумент в начало списка мусора.
CAS относится к чему-то вроде __ sync_bool_compare_and_swap , который возвращает 1 в случае успеха.
чтобы захватить память аргументов:
while (1) { // concurrency loop
pArg = _pRecycle; // _pRecycle is the global ptr to the head of the available arguments
// POINT A
if (CAS(&_pRecycle, pArg->pNext, pArg)) // change pRecycle to next item if pRecycle hasn't changed.
break; // success
// POINT B
}
// you can now use pArg to pass arguments
чтобы перезапустить память аргументов после завершения:
while (1) { // concurrency loop
pArg->pNext = _pRecycle;
if (CAS(&_pRecycle, pArg, pArg->pNext)) // change _pRecycle to pArg if _pRecycle hasn't changed.
break; // success
}
// you have given the mem back
Существует состояние гонки, если что-то использует и перезапускает pArg, в то время как другой поток заменяется между точками A и B. Если ваша работа занимает много времени, это не будет проблемой. В противном случае вам нужно создать версию заголовка списка ... Для этого вам нужно иметь возможность атомарно изменять две вещи одновременно ... Объединения в сочетании с 64-битным CAS на помощь!
typedef union _RecycleList {
struct {
int iversion;
TArg *pHead;
}
unsigned long n64; // this value is iVersion and pHead at the same time!
} TRecycleList;
TRecycleList _Recycle;
, чтобы получить mem:
while (1) // concurrency loop
{
TRecycleList Old.n64 = _Recycle.n64;
TRecycleList New.n64 = Old.n64;
New.iVersion++;
pArg = New.pHead;
New.pHead = New.pHead->pNext;
if (CAS(&_Recycle.n64, New.n64, Old.n64)) // if version isnt changed we get mem
break; // success
}
чтобы вернуть mem:
while (1) // concurrency loop
{
TRecycleList Old.n64 = _Recycle.n64;
TRecycleList New.n64 = Old.n64;
New.iVersion++;
pArg->pNext = New.pHead;
New.pHead = pArg;
if (CAS(&_Recycle.n64, New.n64, Old.n64)) // if version isnt changed we release mem
break; // success
}
Поскольку в 99,9999999% случаев нет двух потоков, выполняющих код для захвата памяти одновременно, вы получаете отличную производительность. Наши тесты показали, что CAS всего в 2 раза медленнее, чем просто установка _pRecycle = pRecycle-> pNext. 64-битный и 128-битный CAS так же быстр, как 32-битный. В основном это крики. Каждый раз через некоторое время цикл параллелизма будет выполняться дважды, когда два потока на самом деле участвуют в гонке. Один всегда победит, поэтому гонка проходит очень быстро.