二进制信号量和互斥量之间有区别吗?或者它们本质上是相同的?


当前回答

互斥锁只能由获得它的线程释放。 二进制信号量可以由任何线程(或进程)发出信号。

因此,信号量更适合于一些同步问题,如生产者-消费者。

在Windows上,二进制信号量更像事件对象而不是互斥对象。

其他回答

互斥锁只能由获得它的线程释放。 二进制信号量可以由任何线程(或进程)发出信号。

因此,信号量更适合于一些同步问题,如生产者-消费者。

在Windows上,二进制信号量更像事件对象而不是互斥对象。

答案可能取决于目标操作系统。例如,我所熟悉的至少一个RTOS实现允许对单个OS互斥量进行多个连续的“get”操作,只要它们都来自同一个线程上下文中。在允许另一个线程获得互斥量之前,多个get必须被相等数量的put替换。这与二进制信号量不同,对于二进制信号量,无论线程上下文如何,一次只允许一个get。

这种互斥锁背后的思想是,通过一次只允许一个上下文修改数据来保护对象。即使线程获得了互斥量,然后调用进一步修改对象的函数(并在自己的操作周围获得/放置保护互斥量),这些操作仍然应该是安全的,因为它们都发生在单个线程下。

{
    mutexGet();  // Other threads can no longer get the mutex.

    // Make changes to the protected object.
    // ...

    objectModify();  // Also gets/puts the mutex.  Only allowed from this thread context.

    // Make more changes to the protected object.
    // ...

    mutexPut();  // Finally allows other threads to get the mutex.
}

当然,在使用此特性时,必须确保单个线程中的所有访问都是安全的!

我不确定这种方法有多普遍,或者它是否适用于我所熟悉的系统之外。有关这种互斥锁的示例,请参阅ThreadX RTOS。

正如这里许多人提到的,互斥锁用于保护关键代码段(又名临界段)。你将在同一个线程中获得互斥锁(lock),进入临界区,释放互斥锁(unlock)。

在使用信号量时,您可以让一个线程(例如线程a)等待一个信号量,直到另一个线程(例如线程B)完成任何任务,然后为线程a设置信号量以停止等待,并继续其任务。

你可以通过以下方法清楚地记住不同之处:

互斥锁:用于保护关键区域, 互斥锁不能跨进程使用,只能在单个进程中使用 信号量:用于信号资源的可用性。 信号量既可以跨进程使用,也可以跨进程使用。

在看了上面的帖子后,这个概念对我来说很清楚。但仍有一些挥之不去的问题。所以,我写了一小段代码。

当我们试图给出一个信号量而不接收它时,它就会通过。但是,当你试图给出一个互斥量而不获取它时,它会失败。我在Windows平台上进行了测试。启用USE_MUTEX使用MUTEX运行相同的代码。

#include <stdio.h>
#include <windows.h>
#define xUSE_MUTEX 1
#define MAX_SEM_COUNT 1

DWORD WINAPI Thread_no_1( LPVOID lpParam );
DWORD WINAPI Thread_no_2( LPVOID lpParam );

HANDLE Handle_Of_Thread_1 = 0;
HANDLE Handle_Of_Thread_2 = 0;
int Data_Of_Thread_1 = 1;
int Data_Of_Thread_2 = 2;
HANDLE ghMutex = NULL;
HANDLE ghSemaphore = NULL;


int main(void)
{

#ifdef USE_MUTEX
    ghMutex = CreateMutex( NULL, FALSE, NULL);
    if (ghMutex  == NULL) 
    {
        printf("CreateMutex error: %d\n", GetLastError());
        return 1;
    }
#else
    // Create a semaphore with initial and max counts of MAX_SEM_COUNT
    ghSemaphore = CreateSemaphore(NULL,MAX_SEM_COUNT,MAX_SEM_COUNT,NULL);
    if (ghSemaphore == NULL) 
    {
        printf("CreateSemaphore error: %d\n", GetLastError());
        return 1;
    }
#endif
    // Create thread 1.
    Handle_Of_Thread_1 = CreateThread( NULL, 0,Thread_no_1, &Data_Of_Thread_1, 0, NULL);  
    if ( Handle_Of_Thread_1 == NULL)
    {
        printf("Create first thread problem \n");
        return 1;
    }

    /* sleep for 5 seconds **/
    Sleep(5 * 1000);

    /*Create thread 2 */
    Handle_Of_Thread_2 = CreateThread( NULL, 0,Thread_no_2, &Data_Of_Thread_2, 0, NULL);  
    if ( Handle_Of_Thread_2 == NULL)
    {
        printf("Create second thread problem \n");
        return 1;
    }

    // Sleep for 20 seconds
    Sleep(20 * 1000);

    printf("Out of the program \n");
    return 0;
}


int my_critical_section_code(HANDLE thread_handle)
{

#ifdef USE_MUTEX
    if(thread_handle == Handle_Of_Thread_1)
    {
        /* get the lock */
        WaitForSingleObject(ghMutex, INFINITE);
        printf("Thread 1 holding the mutex \n");
    }
#else
    /* get the semaphore */
    if(thread_handle == Handle_Of_Thread_1)
    {
        WaitForSingleObject(ghSemaphore, INFINITE);
        printf("Thread 1 holding semaphore \n");
    }
#endif

    if(thread_handle == Handle_Of_Thread_1)
    {
        /* sleep for 10 seconds */
        Sleep(10 * 1000);
#ifdef USE_MUTEX
        printf("Thread 1 about to release mutex \n");
#else
        printf("Thread 1 about to release semaphore \n");
#endif
    }
    else
    {
        /* sleep for 3 secconds */
        Sleep(3 * 1000);
    }

#ifdef USE_MUTEX
    /* release the lock*/
    if(!ReleaseMutex(ghMutex))
    {
        printf("Release Mutex error in thread %d: error # %d\n", (thread_handle == Handle_Of_Thread_1 ? 1:2),GetLastError());
    }
#else
    if (!ReleaseSemaphore(ghSemaphore,1,NULL) )      
    {
        printf("ReleaseSemaphore error in thread %d: error # %d\n",(thread_handle == Handle_Of_Thread_1 ? 1:2), GetLastError());
    }
#endif

    return 0;
}

DWORD WINAPI Thread_no_1( LPVOID lpParam ) 
{ 
    my_critical_section_code(Handle_Of_Thread_1);
    return 0;
}


DWORD WINAPI Thread_no_2( LPVOID lpParam ) 
{
    my_critical_section_code(Handle_Of_Thread_2);
    return 0;
}

信号量允许您发出“使用资源完成”的信号,即使它从未拥有该资源,这一事实使我认为在信号量的情况下,拥有和发出信号之间存在非常松散的耦合。