时序开销:二进制信号量 vs 互斥锁

3

我在vxWorks平台上运行了一个C++程序,以测试互斥锁和二进制信号量之间的时间差异。下面是原型程序

SEM ID semMutex;
UINT ITER = 10000;
taskIdOne = TASKSPAWN("t1",TASK_PRIORITY_2,0,8192,0,(FUNCPTR)myMutexMethod,0,0);
taskIdTwo = TASKSPAWN("t2",TASK_PRIORITY_2,0,8192,0,(FUNCPTR)myMutexMethod,0,0);
void myMutexMethod(void)
    {
        int i;
        VKI_PRINTF("I'm  (%s)\n",TASKNAME(0) );
        myMutexTimer.start();
        for (i=0; i < ITER; i++)
        {
            MUTEX_LOCK(semMutex,WAIT_FOREVER); 
            ++global;                     
            MUTEX_UNLOCK(semMutex); 
        } 
        myMutexTimer.stop();
        myMutexTimer.show();
    }

在上述程序中存在争用(2个任务正在尝试获取互斥锁)。我的定时器为上述程序打印了37.43毫秒。使用相同的原型,二进制信号量程序仅花费了2.8毫秒。这是可以理解的,因为二进制信号量很轻便,没有像互斥锁(优先级反转、所有权等)那样多的功能。但是,当我移除了一个任务并运行上述程序(没有争用)时。由于没有争用,任务t1只需获取互斥锁,执行临界区然后释放互斥锁。二进制信号量也是如此。关于计时,我得到的互斥锁时间是3.35毫秒,二进制信号量是4毫秒。我很惊讶地发现,在没有争用的情况下,互斥锁比二进制信号量更快。这是否是预期的?还是我漏掉了什么?任何帮助都将不胜感激!

我认为(但不确定)这种测量线程同步机制的方法将无法给出正确的结果,因为计时器也跟踪了等待互斥锁的时间。这不仅取决于互斥锁和信号量的实现方式,还取决于操作系统如何分配程序的时间片。每次运行可能都会有所不同。您已经尝试了多少次?持续多长时间? - Excelcius
我已经运行了大约20次,结果一直很稳定。我正在一个空闲的核心上运行这个程序,以便这个任务不会被当前核心中已经运行的任务干扰。 - Wild Widow
信号量通常不被认为是“轻量级”的。如果一个线程无法获取一个单位,它必须在内核调用上阻塞,就像互斥锁一样。 - Martin James
1个回答

1

由于同一个任务一遍又一遍地获取互斥锁且没有其他任务参与,因此在这种情况下互斥锁可能更快。我猜测互斥锁代码采用了一种快捷方式来实现递归互斥锁调用(即同一任务两次获取相同的互斥锁)。尽管您的代码技术上不是递归互斥锁获取,但由于没有其他任务获取信号量,代码可能使用了相同的快捷方式。

换句话说,您执行以下操作:

1) semTake(semMutex)
2) ++global;
3) semGive(semMutex) // sem owner flag is not changed
4) sameTake(semMutex) // from same task as previous semTake
...

在第四步中,semTake看到sem owner ==当前任务ID(因为sem owner在第一步中被设置,并且从未更改为其他任何内容),因此它只是将信号量标记为已获取并快速跳出。当然,这只是一个猜测,通过快速查看源代码和一些vxworks shell断点可以确认这一点,但我无法做到这一点,因为我不再有访问vxworks的权限。此外,还可以查看semMLib文档,了解互斥递归使用的一些文档。

Chris,你太棒了。解释得非常好。我也相信同样的事情正在发生。因为semOwner每次都是相同的,所以它正在被优化。谢谢,伙计! - Wild Widow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接