等待所有线程定时器callback完成的安全方法

在一次性定时器的情况下,我可以使用信号量等待定时器callback完成。 但是,如果定时器被多次发射,这并没有帮助。 考虑下面的代码:

#include <stdlib.h> #include <stdint.h> #include <stdio.h> #include <signal.h> #include <time.h> #include <unistd.h> #include <pthread.h> #define N 10 void timer_threaded_function(sigval_t si) { uint8_t *shared_resource = si.sival_ptr; sleep(rand() % 7); /* ... manipulate with shared_resource */ return; } int main() { struct sigevent sig_ev = {0}; uint8_t *shared_resource = malloc(123); timer_t timer_id; int i; sig_ev.sigev_notify = SIGEV_THREAD; sig_ev.sigev_value.sival_ptr = shared_resource; sig_ev.sigev_notify_function = timer_threaded_function; sig_ev.sigev_notify_attributes = NULL; timer_create(CLOCK_REALTIME, &sig_ev, &timer_id); for (i = 0; i < N; i++) { /* arm timer for 1 nanosecond */ timer_settime(timer_id, 0, &(struct itimerspec){{0,0},{0,1}}, NULL); /* sleep a little bit, so timer will be fired */ usleep(1); } /* only disarms timer, but timer callbacks still can be running */ timer_delete(timer_id); /* * TODO: safe wait for all callbacks to end, so shared resource * can be freed without races. */ ... free(shared_resource); return 0; } 

timer_delete()只解除定时器(如果它已经被占用)并且释放与定时器资源相关联的定时器。 但定时器callback仍然可以运行。 所以我们不能释放shared_resource,否则可能会出现竞态条件。 有什么办法可以应付这种情况?

我关于引用计数,但它没有帮助,因为我们不知道有多less线程会尝试访问共享资源(定时器超支的原因)。

Solutions Collecting From Web of "等待所有线程定时器callback完成的安全方法"

这是完全不能令人满意的 – (我已经看了,似乎没有什么办法可以发现sigevent(a)没有被解雇,或者(b)正在等待,或者(c)正在运行,或者d)已经完成。

最好我可以建议是一个额外的间接级别,并指向共享资源的静态。 所以:

  static foo_t* p_shared ; .... p_shared = shared_resourse ; ..... sig_ev.sigev_value.sival_ptr = &p_shared ; 

其中foo_t是共享资源的类型。

现在我们可以使用一些原子…在timer_threaded_function()

  foo_t** pp_shared ; foo_t* p_locked ; foo_t* p_shared ; pp_shared = so.sival_ptr ; p_locked = (void*)UINPTR_MAX ; p_shared = atomic_swap(pp_shared, p_locked) ; if (p_shared == p_locked) return ; // locked already. if (p_shared == NULL) return ; // destroyed already. .... proceed to do the usual work ... if (atomic_cmp_swap(pp_shared, &p_locked, p_shared)) return ; // was locked and is now restored assert(p_locked == NULL) ; ... the shared resource needs to be freed ... 

而在控制线程中:

  timer_delete(timer_id) ; // no more events, thank you p_s = atomic_swap(&p_shared, NULL) ; // stop processing events if (p_s == (void*)UINTPTR_MAX) // an event is being processed. if (p_s != NULL) ... the shared resource needs to be freed ... 

当事件线程发现共享资源需要被释放时,它可以自己做,或者向控制线程发出事件已经被处理的信号,以便控制线程可以继续进行。 这主要是品味的问题。

基本上,这是使用原子提供一种锁,其值为三态:NULL <=>销毁; UINTPTR_MAX <=>锁定; 任何其他<=>解锁。

下面是static p_shared ,它必须一直存在,直到timer_threaded_function()完成并且永远不会被再次调用…因为这些正是不可知的事情,所以static p_shared是一个fixture :-(。