c - C Windows 中的单 channel 隧道互斥锁示例

标签 c windows mutex semaphore critical-section

我正在尝试解决经典的“单 channel 隧道”信号量/互斥量问题。 这是我写的代码,但它不起作用,我不明白为什么。 理论上,只有当隧道已经被同向行驶的汽车占用时,来自相反方向的汽车才应该穿过,否则它们应该等待,输出应该是这样的:

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) 来跟踪当前有多少辆汽车正在过桥,正如您所看到的,似乎来自相反方向的汽车同时过桥! enter image description here

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

#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 时,此函数返回 WAIT_ABANDONED

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

信号量更适合锁定隧道。


编辑:

我在第一篇文章中建议使用 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;
}

关于c - C Windows 中的单 channel 隧道互斥锁示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50717773/

相关文章:

windows - 如何使用批处理脚本中的提示符自动启动命令

c++ - 定时开销二进制信号量与互斥量

c++ - 中断正在运行的程序并保存数据

c - C 结构中 malloc'd bool 值的默认值?

windows - 调用 ImpersonateSelf() 是否会取消对同一线程所做的所有安全 token 调整?

c++ - 是否将 unique_lock 用于可以由 lock_guard 完成的任务较慢?

mutex - 获取互斥锁列表?

C 结构到 Swift

c - (U) Ruby 扩展 : rb_gc_mark() and instance variables

windows - 如何回复快速 javascript 测试