Используя атомарные операции gcc, вы можете быстро проверить, изменился ли конфиг (время для сравнения двух целых), и вы можете загрузить новые конфиги ... ВСЕ БЕЗ БЛОКИРОВКИ.
Код Psuedo:
Допустим, у меня была структура конфигурации C. Пусть будет глобальная переменная _pConfig, которая указывает на текущую конфигурацию.
struct C *_pConfig;
для загрузки новой конфигурации:
// allocate a new struct C
struct C *pNewconfig = malloc(sizeof(struct C));
...
// load the pNewconfig struct from disk or something
...
// let the config struct have a next pointer so we can save list of configs for freeing later
pNewconfig->pNext = _pConfig;
// when done loading pNewconfig. change the global. not before done!, else threads see unf data!
// 32 bit assignment is atomic (usually).
// If not atomic on your platform, use __sync_bool_compare_and_swap()
_pConfig = pNewConfig;
// is safe to free old cfgs with 0 use counts. Make sure old is old enough so that there is no chance
// a thread could have been swapped out anywhere between point A and B below (next code section).
for (struct C *pCfg=pNewconfig->pNext ; pCfg ; pCfg=pCfg->pNext) {
// Free last pcfg (!pCfg->pNext) if its not in use and its old enough.
// Don't have to atomically check cUse here since once it changes to zero, its zero forever.
// nBirthday could be clock ticks when the config was created. nNow could be ticks now.
if (!pCfg->pNext && !pCfg->cUse && pCfg->nBirthDay-nNow > OldEnough) {
free(pCfg);
break;
}
}
Теперь, как мы это используем ... Каждый поток должен иметь ptr для структурирования C, с которым был сконфигурирован поток. Если _pConfig изменяется, каждый поток может сказать, сравнивая адрес структуры C потока с текущим. Предположим, что pthread является указателем на данные потока.
while (1) {
// POINT A
struct C *pConfig = _pConfig; // make a copy in case it changes
// super quick check with no locking!
if (pConfig == pThread->pConfig)
break; // no change...get out quick.
__sync_add_and_fetch(&pConfig->cUse, 1); // increment use count of new cfg
// POINT B
__sync_sub_and_fetch(&pThread->pConfig->cUse, 1); // decriment use count of thread's cfg
pThread->pConfig = pConfig; // use new cfg
// do whatever you do with the cfg data
}
Используйте __sync_add, _sub, поскольку несколько потоков могут одновременно получать доступ к новой конфигурации, и это обеспечивает точный счет использования.
Имейте в виду время остановки потока, запланированного между точками A и B. Поскольку временной интервал обычно составляет 1 мс, длительный останов может составлять 10-100 мс, если планировщик не подключен. Если ваши данные конфигурации невелики и меняются только пару раз в час, я мог бы держать их в течение нескольких дней, прежде чем их освободить. Выберите достаточно большое время, чтобы вы знали, что нет остановленных потоков. Не беспокойтесь, если поток по какой-то причине не проверял новую конфигурацию в течение длительного времени ... счетчик использования будет> 1, и он не будет освобожден.