c windows 中的单通道隧道互斥锁示例

single lane tunnel mutex example in c windows

我正在尝试解决经典的 "single lane tunnel" semaphore/mutex 问题。 这是我写的代码,但它不起作用,我不明白为什么。 理论上,只有当隧道已经被同向行驶的汽车占用时,来自相反方向的汽车才应该通过,否则他们应该等待,输出应该是这样的:

car1_leftToRight crossing
car2_leftToRight crossing
car1_leftToRight end crossing
car2_leftToRight end crossing  (ALL cars leftToRight have crossed)
car1_rightToLeft start crossing 
etc..

但我当前的输出是您可以在我附上的图片中看到的输出。 我还创建了一个全局变量 (globalCarsCrossing) 来跟踪当前有多少辆汽车正在过桥,正如您所看到的,似乎来自相反方向的汽车同时过桥!

你对我做错了什么有什么建议吗?

#define UNICODE
#define _UNICODE
#define _CRT_SECURE_NO_WARNINGS

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <WinBase.h>
#include <process.h> //include for function _beginthreadex

int globalCarsCrossing = 0;

typedef struct {
    int numTraversingCar;
    int crossingTime;
    int arrivalTime; //0 - arrivalTime -> random number, how much time pass between cars arrival
    HANDLE mutex;
    LPHANDLE bridgeMutexPtr;
    int direction;
} ThParams;

DWORD WINAPI thFunction(LPVOID thParamsPtr);
void crossBridge();

int _tmain(int argc, LPTSTR argv[])
{
    int timeL2R, timeR2L, timeARRIVAL, nL2R, nR2L;
    LPHANDLE thL2Rarray, thR2Larray;
    ThParams paramsL2R, paramsR2L;
    HANDLE bridgeMutex;

    if (argc < 6) 
    {
        _ftprintf(stderr, _T("missing parameters: <timeL2R> <timeR2L> <timeARRIVAL> <nL2R> <nR2L>\n"));
        exit(EXIT_FAILURE);
    }

    timeL2R = _ttoi(argv[1]); //WINDOWS version of "atoi"
    timeR2L = _ttoi(argv[2]);
    timeARRIVAL = _ttoi(argv[3]);
    nL2R = _ttoi(argv[4]);
    nR2L = _ttoi(argv[5]);

    //allocates all threads array
    thL2Rarray = (LPHANDLE)malloc(nL2R * sizeof(HANDLE));
    thR2Larray = (LPHANDLE)malloc(nR2L * sizeof(HANDLE));

    //initialize all parameters
    bridgeMutex = CreateMutex(NULL, FALSE, NULL);

    //create structs for threads
    paramsL2R.mutex = CreateMutex(NULL, FALSE, NULL);
    paramsL2R.bridgeMutexPtr = &bridgeMutex;
    paramsL2R.arrivalTime = timeARRIVAL;
    paramsL2R.numTraversingCar = 0;
    paramsL2R.crossingTime = timeL2R;
    paramsL2R.direction = 0;

    //paramsR2L.criticalSectionPtr = &criticalSectionR2L;
    paramsR2L.mutex = CreateMutex(NULL, FALSE, NULL);
    paramsR2L.bridgeMutexPtr = &bridgeMutex;
    paramsR2L.arrivalTime = timeARRIVAL;
    paramsR2L.numTraversingCar = 0;
    paramsR2L.crossingTime = timeR2L;
    paramsR2L.direction = 1;

    //create L2R threads 
    for (int i = 0; i<nL2R; i++)
        thL2Rarray[i] = CreateThread(NULL, 0, thFunction, &paramsL2R, 0, NULL);
    //create R2L threads 
    for (int i = 0; i<nR2L; i++)
        thR2Larray[i] = CreateThread(NULL, 0, thFunction, &paramsR2L, 0, NULL);

    //wait for ALL threads to return
    WaitForMultipleObjects(nL2R, thL2Rarray, TRUE, INFINITE);
    WaitForMultipleObjects(nR2L, thR2Larray, TRUE, INFINITE);
    _tprintf(_T("all threads are returned\n"));

    //closa all thread handle
    for (int i = 0; i<nL2R; i++)
        CloseHandle(thL2Rarray[i]);
    for (int i = 0; i<nR2L; i++)
        CloseHandle(thR2Larray[i]);

    ////free and release everything
    free(thL2Rarray);
    free(thR2Larray);
    CloseHandle(bridgeMutex);
    CloseHandle(paramsR2L.mutex);
    CloseHandle(paramsL2R.mutex);

    return 0;
}

DWORD WINAPI thFunction(LPVOID thParamsPtr)
{
    ThParams *paramsPtr = (ThParams *)thParamsPtr;

    WaitForSingleObject(paramsPtr->mutex, INFINITE);
        paramsPtr->numTraversingCar = paramsPtr->numTraversingCar + 1;

        if (paramsPtr->numTraversingCar == 1)
            WaitForSingleObject(*(paramsPtr->bridgeMutexPtr), INFINITE);

        globalCarsCrossing++;
        _tprintf(_T("%d crossing direction: %d, TOT_cars_from_this_direction: %d,  GLOBAL_CARS_CROSSING: %d\n"), GetCurrentThreadId(), paramsPtr->direction, paramsPtr->numTraversingCar, globalCarsCrossing);
    ReleaseMutex(paramsPtr->mutex);

    crossBridge();

    WaitForSingleObject(paramsPtr->mutex, INFINITE);
        paramsPtr->numTraversingCar = paramsPtr->numTraversingCar - 1;
        globalCarsCrossing--;
        _tprintf(_T("%d end crossing direction: %d, TOT_cars_from_this_direction: %d,  GLOBAL_CARS_CROSSING %d\n"), GetCurrentThreadId(), paramsPtr->direction, paramsPtr->numTraversingCar, globalCarsCrossing);
        if (paramsPtr->numTraversingCar == 0) {
            _tprintf(_T("RELEASED\n"));
            ReleaseMutex(*(paramsPtr->bridgeMutexPtr));
        }
    ReleaseMutex(paramsPtr->mutex);

    return 0;
}

问题来自您的 WaitForSingleObject 电话:

Return code: WAIT_ABANDONED 0x00000080L

The specified object is a mutex object that was not released by the thread that owned the mutex object before the owning thread terminated. Ownership of the mutex object is granted to the calling thread and the mutex state is set to nonsignaled.

If the mutex was protecting persistent state information, you should check it for consistency.

线程 2944 在隧道上获得了互斥量,使其汽车通过并完成,没有释放互斥量。

当线程3560调用WaitForSingleObject时,这个函数returnsWAIT_ABANDONED

您的代码无法执行您想要的操作,因为线程占用的互斥体必须由同一线程释放

信号量更适合锁定隧道。


编辑:

我首先post我建议使用CriticalSection,但是像Mutex一样,一个CriticalSection必须由同一个线程获取和释放。


示例实现:

#define UNICODE
#define _UNICODE
#define _CRT_SECURE_NO_WARNINGS

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <WinBase.h>
#include <process.h> //include for function _beginthreadex

int globalCarsCrossing = 0;

typedef struct {
    int numTraversingCar;
    int crossingTime;
    int arrivalTime; //0 - arrivalTime -> random number, how much time pass between cars arrival
    HANDLE mutex;
    HANDLE bridgeSem;
    int direction;
} ThParams;

DWORD WINAPI thFunction(LPVOID thParamsPtr);
void crossBridge();

int _tmain(int argc, LPTSTR argv[])
{
    int timeL2R, timeR2L, timeARRIVAL, nL2R, nR2L;
    LPHANDLE thL2Rarray, thR2Larray;
    ThParams paramsL2R, paramsR2L;
    HANDLE bridgeSem;

    if (argc < 6) 
    {
        _ftprintf(stderr, _T("missing parameters: <timeL2R> <timeR2L> <timeARRIVAL> <nL2R> <nR2L>\n"));
        exit(EXIT_FAILURE);
    }

    timeL2R = _ttoi(argv[1]); //WINDOWS version of "atoi"
    timeR2L = _ttoi(argv[2]);
    timeARRIVAL = _ttoi(argv[3]);
    nL2R = _ttoi(argv[4]);
    nR2L = _ttoi(argv[5]);

    //allocates all threads array
    thL2Rarray = (LPHANDLE)malloc(nL2R * sizeof(HANDLE));
    thR2Larray = (LPHANDLE)malloc(nR2L * sizeof(HANDLE));

    //initialize all parameters
    bridgeSem = CreateSemaphore(NULL, 1, 1, NULL);

    //create structs for threads
    paramsL2R.mutex = CreateMutex(NULL, FALSE, NULL);
    paramsL2R.bridgeSem = bridgeSem;
    paramsL2R.arrivalTime = timeARRIVAL;
    paramsL2R.numTraversingCar = 0;
    paramsL2R.crossingTime = timeL2R;
    paramsL2R.direction = 0;

    //paramsR2L.criticalSectionPtr = &criticalSectionR2L;
    paramsR2L.mutex = CreateMutex(NULL, FALSE, NULL);
    paramsR2L.bridgeSem = bridgeSem;
    paramsR2L.arrivalTime = timeARRIVAL;
    paramsR2L.numTraversingCar = 0;
    paramsR2L.crossingTime = timeR2L;
    paramsR2L.direction = 1;

    //create L2R threads 
    for (int i = 0; i<nL2R; i++)
        thL2Rarray[i] = CreateThread(NULL, 0, thFunction, &paramsL2R, 0, NULL);
    //create R2L threads 
    for (int i = 0; i<nR2L; i++)
        thR2Larray[i] = CreateThread(NULL, 0, thFunction, &paramsR2L, 0, NULL);

    //wait for ALL threads to return
    WaitForMultipleObjects(nL2R, thL2Rarray, TRUE, INFINITE);
    WaitForMultipleObjects(nR2L, thR2Larray, TRUE, INFINITE);
    _tprintf(_T("all threads are returned\n"));

    //closa all thread handle
    for (int i = 0; i<nL2R; i++)
        CloseHandle(thL2Rarray[i]);
    for (int i = 0; i<nR2L; i++)
        CloseHandle(thR2Larray[i]);

    ////free and release everything
    free(thL2Rarray);
    free(thR2Larray);
    CloseHandle(bridgeSem);
    CloseHandle(paramsR2L.mutex);
    CloseHandle(paramsL2R.mutex);

    return 0;
}

DWORD WINAPI thFunction(LPVOID thParamsPtr)
{
    ThParams *paramsPtr = (ThParams *)thParamsPtr;

    WaitForSingleObject(paramsPtr->mutex, INFINITE);
        paramsPtr->numTraversingCar = paramsPtr->numTraversingCar + 1;

        if (paramsPtr->numTraversingCar == 1)
            WaitForSingleObject(paramsPtr->bridgeSem, INFINITE);

        globalCarsCrossing++;
        _tprintf(_T("%d crossing direction: %d, TOT_cars_from_this_direction: %d,  GLOBAL_CARS_CROSSING: %d\n"), GetCurrentThreadId(), paramsPtr->direction, paramsPtr->numTraversingCar, globalCarsCrossing);
    ReleaseMutex(paramsPtr->mutex);

    crossBridge();

    WaitForSingleObject(paramsPtr->mutex, INFINITE);
        paramsPtr->numTraversingCar = paramsPtr->numTraversingCar - 1;
        globalCarsCrossing--;
        _tprintf(_T("%d end crossing direction: %d, TOT_cars_from_this_direction: %d,  GLOBAL_CARS_CROSSING %d\n"), GetCurrentThreadId(), paramsPtr->direction, paramsPtr->numTraversingCar, globalCarsCrossing);
        if (paramsPtr->numTraversingCar == 0) {
            _tprintf(_T("RELEASED\n"));
            ReleaseSemaphore(paramsPtr->bridgeSem, 1, NULL);
        }
    ReleaseMutex(paramsPtr->mutex);

    return 0;
}