You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Win32多线程编程中写线程调用WaitForSingleObject阻塞问题求解

问题根源

你的实现是读优先的读写锁逻辑,核心问题是写线程饥饿,具体触发逻辑如下:

  • 只要有至少1个读线程持有hMutexRW,后续新到达的读线程不需要申请hMutexRW,直接可以进入读操作
  • 若读线程持续不断发起读请求,currentSemaphoreR会始终大于0,hMutexRW永远不会被释放,阻塞在WaitForSingleObject(hMutexRW, INFINITE)的写线程永远拿不到锁,陷入永久阻塞

除此之外你的代码还有两个隐含bug:

  • 写线程中waitTime、lastTime变量未初始化就直接使用,会输出随机值
  • 读线程打开文件失败时没有提前处理读计数,可能导致锁泄漏
解决方法

修改为写优先逻辑,增加写等待计数,当有写线程等待时,禁止新的读线程进入读区,避免写线程被无限饿死。修改后的完整代码如下:

const int nThreadWriting = 5;
const int nThreadReading = 5;
const int nBufSize = 100;
const int maxWritten = 10;
const int timeRange = 3;
HANDLE hMutexRW, hMutexR, hMutexN;
int currentSemaphoreR = 0;
int numberWritten = 0;
// 新增:等待中的写线程计数
int waitingWriters = 0;

unsigned __stdcall ThreadReading(void *pV)
{
    HANDLE hFile;
    int waitTime, lastTime;
    char buf[nBufSize];
    int* ptr = (int*)pV;
    srand(*ptr);
    while (1)
    {
        WaitForSingleObject(hMutexN, INFINITE);
        if (numberWritten >= maxWritten)
        {
            ReleaseMutex(hMutexN);
            return 0;
        }
        ReleaseMutex(hMutexN);

        waitTime = rand() % timeRange+1;
        lastTime = rand() % timeRange+1;

        // 先检查有没有写线程在等,有就先让写线程
        WaitForSingleObject(hMutexR, INFINITE);
        while (waitingWriters > 0) {
            ReleaseMutex(hMutexR);
            Sleep(1); // 让出时间片给写线程
            WaitForSingleObject(hMutexR, INFINITE);
        }
        if (currentSemaphoreR == 0)
        {
            WaitForSingleObject(hMutexRW, INFINITE);
            sprintf_s(buf, nBufSize, "%d R GET MUTEX_RW\n", GetCurrentThreadId());
            std::cout << buf << std::endl;
        }
        currentSemaphoreR++;
        ReleaseMutex(hMutexR);

        hFile = CreateFile(TEXT("nice.txt"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
        {
            std::cout << "cannot open file" << std::endl;
            // 读失败也要正确释放读计数
            WaitForSingleObject(hMutexR, INFINITE);
            currentSemaphoreR--;
            if (currentSemaphoreR == 0)
                ReleaseMutex(hMutexRW);
            ReleaseMutex(hMutexR);
            continue;
        }
        sprintf_s(buf, nBufSize, "%d R %d %d\n", GetCurrentThreadId(), waitTime, lastTime);
        std::cout << buf <<std::endl;
        CloseHandle(hFile);

        WaitForSingleObject(hMutexR, INFINITE);
        currentSemaphoreR--;
        if (currentSemaphoreR == 0)
            ReleaseMutex(hMutexRW);
        ReleaseMutex(hMutexR);
    }
    return 0;
}

unsigned __stdcall ThreadWriting(void *pV)
{
    int waitTime, lastTime;
    char buf[nBufSize];
    int* ptr = (int*)pV;
    srand(*ptr);
    HANDLE hFile;
    while (1)
    {
        WaitForSingleObject(hMutexN, INFINITE);
        if (numberWritten >= maxWritten)
        {
            ReleaseMutex(hMutexN);
            return 0;
        }
        numberWritten++;
        ReleaseMutex(hMutexN);

        // 标记有写线程在等待
        WaitForSingleObject(hMutexR, INFINITE);
        waitingWriters++;
        ReleaseMutex(hMutexR);

        WaitForSingleObject(hMutexRW, INFINITE);

        // 已经拿到锁,减少等待计数
        WaitForSingleObject(hMutexR, INFINITE);
        waitingWriters--;
        ReleaseMutex(hMutexR);

        // 初始化变量,修复未初始化bug
        waitTime = rand() % timeRange+1;
        lastTime = rand() % timeRange+1;

        hFile = CreateFile(TEXT("nice.txt"), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
        {
            std::cout << "cannot open file" << std::endl;
            ReleaseMutex(hMutexRW);
            return 1;
        }
        SetFilePointer(hFile, 0, NULL, FILE_END);
        sprintf_s(buf, nBufSize, "%d W %d %d\n", GetCurrentThreadId(), waitTime, lastTime);
        WriteFile(hFile, buf, strlen(buf), NULL, NULL);
        CloseHandle(hFile);
        ReleaseMutex(hMutexRW);
    }
    return 0;
}

int main()
{
    HANDLE hFile;
    hFile = CreateFile(TEXT("nice.txt"), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    CloseHandle(hFile);
    DWORD IDThread[nThreadWriting+ nThreadReading];
    HANDLE hThread[nThreadWriting + nThreadReading];
    int num[nThreadWriting + nThreadReading];
    for (int i = 0; i < nThreadWriting + nThreadReading; i++)
        num[i] = i;
    hMutexRW = CreateMutex(NULL, FALSE, NULL);
    hMutexR = CreateMutex(NULL, FALSE, NULL);
    hMutexN = CreateMutex(NULL, FALSE, NULL);
    if (!hMutexRW || !hMutexR || !hMutexN)
        return 1;
    for (int i = 0; i < nThreadReading; i++)
    {
        hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadReading, num + i, 0, (unsigned int*)&IDThread[i]);
        if (!hThread[i])
            ExitProcess(3);
    }
    for (int i = nThreadReading; i < nThreadWriting + nThreadReading; i++)
    {
        hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadWriting, num + i, 0, (unsigned int*)&IDThread[i]);
        if (!hThread[i])
            ExitProcess(3);
    }
    WaitForMultipleObjects(nThreadWriting + nThreadReading, hThread, TRUE, INFINITE);
    for (int i = 0; i < nThreadWriting + nThreadReading; i++)
        CloseHandle(hThread[i]);
    CloseHandle(hMutexRW);
    CloseHandle(hMutexR);
    CloseHandle(hMutexN);
    return 0;
}
额外优化建议

如果不需要手动实现读写锁,Win32原生提供了SRWLock读写锁API,使用更简单,性能也更高,不需要自行维护计数和互斥量逻辑。

内容的提问来源于stack exchange,提问作者strwake

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 16:15:06