2016-06-08 42 views
0

我的程序的每个线程都有自己的日志文件。在我的SIGHUP处理程序中,我想通知那些线程,当新的日志消息到达时,他们需要重新打开它们的日志文件。在多线程中延迟无效的东西

我想要一个基于标志和计数器的无锁解决方案。 (我有一个线程本地上下文结构用于其他目的,所以我可以在那里添加新的字段)。

如果只是一个记录线程,我会做:

static int need_reopen = 0; 

void sighancont(int signo) 
... 
    case SIGHUP: 
    need_reopen = 1; 
    break; 
... 
} 

void log(char *msg) { 
    if (need_reopen) { 
     need_reopen = 0; 
     reopen_log(); 
    } 
    ... 
} 

当然,如果有多个线程的日志记录,一个简单的标志不会做。我正在考虑这样的事情:

static volatile int reopen_counter = 0; 

void sighancont(int signo) 
... 
    case SIGHUP: 
    __sync_fetch_and_add(&reopen_counter, 1); 
    break; 
... 
} 

void log(struct ctx_st *ctx, char *msg) { 
    int c = reopen_counter; 
    if (ctx->reopen_counter != c) { 
     ctx->reopen_counter = c; 
     reopen_log(); 
    } 
    ... 
} 

这种方式日志记录线程应该赶上全局计数器。如果程序多次收到SIGHUP,日志文件将只能重新打开一次。

我看到唯一的方法来打破这个 - 发送SIGHUP〜40亿次。

是否有更好的(但仍然很简单)算法,例如:参考计数?

+1

您的代码中有数据竞争。如果值不是原子的,则无法从多个线程读取和修改相同的变量。 – SergeyA

+0

@SergeyA in sighancont()或log()?如果你的意思是2个concurren SIGHUPS,我可以和 – basin

+0

住在一起:) – SergeyA

回答

0

您的解决方案简单高效。这是一个seqlock。

的几个注意事项,从评论清除可能出现的混乱:

  1. 有没有“原子变量”,但原子指令。 std :: atomic和friends,只是原子操作上的语法糖 - 你在那里完全可以。
  2. 计数器不必是易失性的,但访问必须是。当你写atomic_read(x)你实际上说*(volatile int*)&x。 volatile限定符会导致从内存中完成对变量的所有访问,而您不一定需要这样做。 但是,在这里,你完全没问题,因为你把变量读入本地。
  3. 如果这是唯一一个作家(如果您删除了volatile,请不要忘记将它改为atomic_write),您可以非自动更新计数器。这将是一个非常小的性能改进。
  4. 这里唯一的代价是在更新计数器后必须为主存储器访问支付的日志线程。您应该预计200个周期左右(其他NUMA节点上的x2)