实施关键部分

时间:2012-01-30 16:14:43

标签: c multithreading gcc atomic critical-section

创建关键部分的更好,更快的方法是什么?

使用二进制信号量,在sem_wait和sem_post之间 或者使用原子操作:

#include <sched.h>

void critical_code(){
    static volatile bool lock = false;

    //Enter critical section
    while ( !__sync_bool_compare_and_swap (&lock, false, true ) ){
        sched_yield();
    }

    //...

    //Leave critical section
    lock = false;
}

4 个答案:

答案 0 :(得分:3)

无论使用何种方法,代码中性能最差的问题与您使用的锁类型无关,而是锁定代码而不是数据。

话虽如此,没有理由像你那样推出自己的自旋锁。如果你想要一个自旋锁,可以使用pthread_spin_lock,如果你想要一个可以在争用时屈服于其他进程的锁,可以使用pthread_mutex_locksem_wait(带有二进制信号量)。您编写的代码是“{strong>两个世界中最差的”使用sched_yield的代码。对sched_yield的调用将确保在存在锁争用和cpu负载的情况下,锁等待至少几毫秒(可能是整个调度时间片),并且当存在争用时它会烧掉100%的cpu但是没有CPU负载(例如,由于锁定器在IO中被阻塞)。如果您想获得自旋锁的任何好处,您需要在不进行任何系统调用的情况下进行旋转。如果你想要产生cpu的任何好处,你应该使用一个适当的同步原语,它将使用(在Linux上)futex(或等效的)操作来准确地产生,直到锁可用 - 没有更短,没有更长的时间。

如果偶然发现了所有这些,甚至不要考虑自己写锁......

答案 1 :(得分:2)

如果对锁的争用很少和/或从未长时间保持,旋转锁的性能会更好。否则你最好使用阻止而不是旋转的锁。当然有混合锁可以旋转几次,如果无法获得锁,那么它们就会阻塞。

哪个更适合您取决于您​​的应用程序。只有你能回答这个问题。

答案 2 :(得分:2)

你在gcc文档中看起来不够深入。这种类型的锁的正确内置函数是__sync_lock_test_and_set__sync_lock_release。这些完全可以保证您需要这样的东西。就新的C11标准而言,这将是具有操作atomic_flagatomic_flag_test_and_set的{​​{1}}类型。

正如R.已经指出的那样,将atomic_flag_clear放入循环中,确实是一个坏主意。

如果临界区内的代码只是一些周期,那么它的执行落在调度切片边界上的概率很小。将被阻止主动旋转的线程数最多为处理器数减1。如果在没有立即获得锁定的情况下立即执行执行,则所有这些都不成立。如果您对锁产生了真正的争用,那么您将拥有大量的上下文切换,这将使您的系统几乎处于暂停状态。

答案 3 :(得分:0)

正如其他人指出的那样,它并不是关于锁定代码有多快。这是因为一旦使用“xchg reg,mem”启动锁定序列,锁定信号将通过高速缓存向下发送到所有总线上的设备。当最后一个设备确认它将保持并确认时 - 这可能需要数百甚至数千个时钟周期才能执行实际的交换。如果您的最慢设备是经典的PCI卡,它的总线速度为33 MHz,约为CPU内部时钟的百分之一。 PCI设备(如果有效)需要几个时钟周期(@ 33 MHz)才能响应。在此期间,CPU将等待确认返回。

大多数自旋锁可能用于设备驱动程序,其中例程不会被操作系统抢占,但可能会被更高级别的驱动程序中断。

一个关键部分实际上只是一个自旋锁,但与操作系统接口,因为它可能会被抢先一步。