Многопоточное программирование в Win32

#c #windows #multithreading #deadlock

Вопрос:

Я использую мьютекс для синхронизации записи и чтения файлов. Я создаю несколько потоков для чтения файла и несколько потоков для записи файла. Но поток записи иногда блокируется в WaitForSingleObject(hMutexRW,БЕСКОНЕЧНЫЙ). Я новичок в программировании потоков Win32 и не знаю, в чем проблема. Я думаю, что проблема в мьютексе, и я хочу знать, как решить эту проблему.

 const int nThreadWriting = 5;
const int nThreadReading = 5;
const int nBufSize = 100;
const int maxWritten = 10;
const int timeRange = 3;
HANDLE hMutexRW, hMutexW, hMutexR, hMutexN;
int currentSemaphoreR = 0;
int numberWritten = 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(hMutexW, INFINITE);
        WaitForSingleObject(hMutexR, INFINITE);
        if (currentSemaphoreR == 0)
        {
            WaitForSingleObject(hMutexRW, INFINITE);
            sprintf_s(buf, nBufSize, "%d R GET MUTEX_RWn", GetCurrentThreadId());
            std::cout << buf << std::endl;
        }
        currentSemaphoreR  ;
        ReleaseMutex(hMutexR);
        ReleaseMutex(hMutexW);

        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;
        }
        sprintf_s(buf, nBufSize, "%d R %d %dn", 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(hMutexW, INFINITE);
        WaitForSingleObject(hMutexRW, INFINITE);
        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;
            return 1;
        }
        SetFilePointer(hFile, 0, NULL, FILE_END);
        sprintf_s(buf, nBufSize, "%d W %d %dn", GetCurrentThreadId(), waitTime, lastTime);
        WriteFile(hFile, buf, strlen(buf), NULL, NULL);
        CloseHandle(hFile);
        ReleaseMutex(hMutexRW);
        ReleaseMutex(hMutexW);
    }
    return 0;
}

int main()
{
    HANDLE hFile;
    hFile = CreateFile(TEXT("nice.txt"), GENERIC_READ, FILE_SHARE_READ, 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);
    hMutexW = CreateMutex(NULL, FALSE, NULL);
    hMutexN = CreateMutex(NULL, FALSE, NULL);
    if (!hMutexRW || !hMutexW || !hMutexR)
        return 1;
    for (int i = 0; i < nThreadReading; i  )
    {
        hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadReading, num   i, 0, (unsigned int*)amp;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*)amp;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(hMutexW);
    CloseHandle(hMutexN);
    return 0;
}
 

Комментарии:

1. Осторожнее с srand и. rand Они не гарантируют потокобезопасность.

2. У вас там больше мьютексов, чем мне было бы удобно. Я не присматривался очень внимательно, но вам придется быть очень осторожным, чтобы не оказаться в тупике, когда один поток удерживает мьютекс A, заблокированный в ожидании B, а другой поток удерживает мьютекс B во время ожидания A или аналогичной гадости.

3. Спасибо за ваш ответ! Я нахожу проблему! Это потому, что мьютекс должен быть выпущен в потоке, которому принадлежал Мьютекс, поэтому вместо этого я использую Семафор. И спасибо за то, что вы напомнили, что rand это небезопасная функция потока, хотя я мало представляю, к какому результату это может привести…

4. Одна из возможностей- rand перейти к следующему номеру в последовательности генератора, а затем вернуть значение. Если два потока входят одновременно, вы можете получить последовательность продвижения потока 1, последовательность продвижения потока 2, поток 1 возвращает значение, поток 2 возвращает значение. Потоки 1 и 2 вернули одно и то же значение. Если продвижение последовательности достаточно сложно и прерывается другим потоком на полпути, Кром знает только, что произойдет.

5. Имейте в виду, что вы rand могли бы быть очень простыми и всегда возвращать 4 . Это довольно слабо конкретизированная функция.