在用户级线程库中实现互斥锁

时间:2011-09-23 06:17:11

标签: c multithreading mutex

我正在开发一个用户级线程库作为项目的一部分。我想出了一种实现互斥的方法。我希望在继续之前看到你的观点。基本上,我需要在我的库中实现3个函数

mutex_init,mutex_lock和mutex_unlock

我认为我的mutex_t结构看起来像

 typedef struct 
 {
   int available; //indicates whether the mutex is locked or unlocked
   queue listofwaitingthreads;
   gtthread_t owningthread; 
 }mutex_t;

在我的mutex_lock函数中,我将首先检查互斥锁是否在while循环中可用。如果不是,我将为下一个要执行的线程生成处理器。

在我的mutex_unlock函数中,我将检查所有者线程是否是当前线程。如果是,我将设置为0.

这是怎么回事?还有,僵局呢?我应该在用户级库中处理这些条件,还是应该让应用程序员正确编写代码?

4 个答案:

答案 0 :(得分:8)

这不起作用,因为你有竞争条件。如果2个线程同时尝试捕获锁,则两者都会看到available == 0,并且两者都认为他们成功获取了互斥锁。

如果要正确执行此操作,并且不使用已存在的锁,则必须访问TAS,CAS等硬件操作。

有些算法可以在没有这些硬件支持的情况下为您提供互斥,但是它们会做出一些错误的假设。有关这方面的更多细节,我强烈建议阅读Herlihy和Shavit的多处理器编程艺术,第7章。

你不应该担心这个级别的死锁 - 互斥锁应该足够简单,并且有一些假设,使用它们的程序员应该小心不要导致死锁(高级互斥锁可以检查自死锁,意思是调用锁两次而不调用中间解锁的线程。)

答案 1 :(得分:0)

不仅你必须进行原子操作来读取和修改标志(正如Eran指出的那样),你还必须注意你的队列能够进行并发访问。这并非完全无足轻重,有点像鸡蛋和鸡蛋问题。

但是如果你真的通过旋转来实现它,你甚至不需要有这样的队列。然而,锁的访问顺序主要是随机的。

可能只是屈服也是不够的,如果线程持有超过一些处理器周期的锁,这可能会非常昂贵。考虑使用等待时间值较短的nanosleep

答案 2 :(得分:0)

通常,互斥锁实现应如下所示:

锁定:

while (trylock()==failed) {
    atomic_inc(waiter_cnt);
    atomic_sleep_if_locked();
    atomic_dec(waiter_cnt);
}

的tryLock:

return atomic_swap(&lock, 1);

解锁:

atomic_store(&lock, 0);
if (waiter_cnt) wakeup_sleepers();

如果你想要递归的互斥锁,可以同步自己的破坏的互斥锁(例如,一旦你获得锁定就释放互斥锁是安全的)等等,事情会变得更加复杂。

请注意,atomic_sleep_if_lockedwakeup_sleepers对应于Linux上的FUTEX_WAITFUTEX_WAKE操作。其他原子可能是CPU指令,但可能是系统调用或内核辅助用户空间函数代码,如Linux / ARM和0xffff0fc0原子比较和交换调用。

答案 3 :(得分:0)

您不需要用户级线程库的原子指令,因为所有线程都将是同一进程的用户级线程。实际上,当您的进程被赋予执行时间片时,您在该时间片期间运行多个线程但在同一处理器上运行。因此,同时没有两个线程将在库函数中。考虑到互斥锁的功能已经存在于库中,因此可以保证互斥。