我在C中编写多线程程序,目前我需要在每次更改配置文件时重启程序,我的应用程序也支持标准SIGHUP信号重新加载配置,但这需要手动干预。
为了解决这个问题,我编写了一个单独的线程来读取配置文件并加载它,并继续监视此文件以进行任何更改。
问题是,如何安全地通知其他线程有关配置更改的信息并且不会因巨大的互斥锁而影响性能。
我正在考虑为每个配置更改设置一个版本号,这样我只需要锁定config_ver变量,并保持旧配置可以访问较慢的线程。
任何想法都会受到赞赏。
答案 0 :(得分:1)
使用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保存到配置了线程的struct C.如果_pConfig发生了变化,每个线程都可以通过比较线程的struct 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 ms,因此长时间停顿可能为10-100 ms,除非调度程序已被清除。如果您的配置数据很小并且每小时只更改几次,我可能会在释放它之前保留数天。选择一个足够长的时间,你知道没有停滞的线程。如果由于某种原因线程长时间未检查新配置,请不要担心...使用次数将是> 1,它不会被释放。