锁定api的正确惯例是什么?

时间:2013-01-27 18:52:19

标签: c multithreading

问题在于编码风格。如何创建外部功能但处理锁定的功能。

void Timer_start(Timer *t){ //this is a user facing function
    if (t->state == Timer_paused){ 
        Timer_resume(t);               
    } else {
        Timer_initialize(t);
    }
}

void Timer_resume(Timer *t){ //this is also a user facing function
    Lock_acquire(t->lock);
    //...do work 
    Lock_release(t->lock);
}

void Timer_initialize(Timer *t){ //and so is this
    //...do work (just to illustrate the the functions arent necessarily simply wrapped with the locking functions)
    Lock_acquire(t->lock);
    //...do work 
    Lock_release(t->lock);
}

在示例中,Timer_start应该包含在Lock_acquireLock_release中,就像其他两个函数一样,因为在状态检查后可能会立即中断。问题是我无法将函数包装在正确的锁定函数中,因为被调用的函数本身会获得锁定。是否有编码风格可以很好地处理这个问题?

2 个答案:

答案 0 :(得分:2)

为什么不使用本地(静态)函数来执行_resume和_init的内容,然后所有外部函数都只有锁定并调用内部函数。

static void Timer_resume_impl(Timer *t) {
   do work
}
static void Timer_initialize_impl(Timer *t) {
   do work
}

void Timer_start(Timer *t) {
   Lock_acquire(t->lock);
   if (t->state == Timer_paused) {
      Timer_resume_impl(t);
   }
   else {
      Timer_initialize_impl(t);
   } 
   Lock_release(t->lock);
}
void Timer_resume(Timer *t) {
   Lock_acquire(t->lock);
   Timer_resume_impl(t);
   Lock_release(t->lock);
}
... 

答案 1 :(得分:1)

您可以实施Timer_resume_unlockedTimer_start_unlocked,这需要调用者负责锁定。然后让Timer_resumeTimer_start成为一个包装器,除了锁定和调用他们的_unlocked对应者之外什么都不做。

您可以自行决定是否将_unlocked个变体作为公共API的一部分,但通常情况下,用户最终会更希望这些变体。

另一个选择是,如果可能能够正确实施API,请为API的调用者保留适当的锁定。通常可以将一个愚蠢的“锁定所有”安全层添加到不使用锁的库中,但是当它变得多余时,从库的内容中删除锁定是不可能的。

或者您可以使用递归锁定(对good arguments有反对意见,但仍有可能)。