【问题标题】:Configuration auto reloading with pthreads使用 pthread 自动重新加载配置
【发布时间】:2011-03-19 17:40:32
【问题描述】:

我正在用C编写多线程程序,目前每次更改配置文件都需要重新启动程序,我的应用程序也支持标准的SIGHUP信号重新加载配置,但这需要手动干预。

为了解决这个问题,我编写了一个单独的线程来读取配置文件并加载它,并持续监控这个文件是否有任何变化。

问题是,如何安全地通知其他线程配置更改,而不影响巨大的互斥锁的性能。

我正在考虑为每个配置更改设置一个版本号,这样我只需要锁定 config_ver 变量更改,并让较慢的线程可以访问旧配置。

任何想法将不胜感激。

【问题讨论】:

    标签: pthreads


    【解决方案1】:

    使用 gcc 原子操作,您可以快速测试配置是否更改(比较两个整数的时间),并且您可以加载新配置...ALL WITHOUT LOCKING。

    伪代码:

    假设我有一个配置结构 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并且不会被释放。

    【讨论】:

    • 如果您在释放旧配置之前等待足够长的时间......或者根本不释放它们,那么引用计数可能是过度的。此解决方案的重要概念是您创建一个新的配置结构,允许线程仍然使用旧的配置结构,直到它们注意到更改。
    猜你喜欢
    • 2010-12-14
    • 1970-01-01
    • 1970-01-01
    • 2010-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-13
    • 1970-01-01
    相关资源
    最近更新 更多