【问题标题】:Implementing mutex in a user level thread library在用户级线程库中实现互斥锁
【发布时间】:2011-09-23 06:17:11
【问题描述】:

我正在开发一个用户级线程库作为项目的一部分。我想出了一种实现互斥锁的方法。在继续之前,我想看看你的意见。基本上,我只需要在我的库中实现 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 函数中,我将首先检查 mutex 在 while 循环中是否可用。如果不是,我将让处理器让下一个线程执行。

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

这是解决方法吗?另外,死锁怎么办?我应该在我的用户级库中处理这些条件,还是应该让应用程序程序员正确编写代码?

【问题讨论】:

  • 如果没有其他线程准备好运行,则屈服是没有操作的,因此您将拥有一个繁忙的循环。您的目标是没有互斥体的平台吗?
  • 在操作等待线程列表时,您不需要另一个互斥锁来锁定它吗?

标签: c multithreading mutex


【解决方案1】:

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

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

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

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

【讨论】:

  • "并且双方都会认为他们成功地获取了互斥锁。"他们不能简单地检查 owningthread 以查看他们是否是真正获得锁的人吗?
  • @LVB 建议按什么顺序设置 owningthread、设置可用和检查 owningthread?
  • 没关系...我开始回忆起这些问题的更多信息,现在看看为什么它不起作用。
【解决方案2】:

不仅您必须执行原子操作来读取和修改标志(正如 Eran 指出的那样),您还必须注意您的队列是否能够进行并发访问。这不是完全微不足道的问题。

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

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

【讨论】:

    【解决方案3】:

    一般来说,互斥锁实现应该如下所示:

    锁定:

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

    试锁:

    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 原子比较和交换调用。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-31
        • 2023-03-22
        • 1970-01-01
        • 2011-07-20
        • 1970-01-01
        • 2014-10-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多