#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 . Это довольно слабо конкретизированная функция.