多线程和等待事件的问题
Problem with multi-threading and waiting on events
我的代码有问题:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <windows.h>
#include <string.h>
#include <math.h>
HANDLE event;
HANDLE mutex;
int runner = 0;
DWORD WINAPI thread_fun(LPVOID lpParam) {
int* data = (int*)lpParam;
for (int j = 0; j < 4; j++) { //this loop necessary in order to reproduce the issue
if ((data[2] + 1) == data[0]) { // if it is last thread
while (1) {
WaitForSingleObject(mutex, INFINITE);
if (runner == data[0] - 1) { // if all other thread reach event break
ReleaseMutex(mutex);
break;
}
printf("Run:%d\n", runner);
ReleaseMutex(mutex);
Sleep(10);
}
printf("Check Done:<<%d>>\n", data[2]);
runner = 0;
PulseEvent(event); // let all other threads continue
}
else { // if it is not last thread
WaitForSingleObject(mutex, INFINITE);
runner++;
ReleaseMutex(mutex);
printf("Wait:<<%d>>\n", data[2]);
WaitForSingleObject(event, INFINITE); // wait till all other threads reach this stage
printf("Exit:<<%d>>\n", data[2]);
}
}
return 0;
}
int main()
{
event = CreateEvent(NULL, TRUE, FALSE, NULL);
mutex = CreateMutex(NULL, FALSE, NULL);
SetEvent(event);
int data[3] = {2,8}; //0 amount of threads //1 amount of numbers
HANDLE t[10000];
int ThreadData[1000][3];
for (int i = 0; i < data[0]; i++) {
memcpy(ThreadData[i], data, sizeof(int) * 2); // copy amount of threads and amount of numbers to the threads data
ThreadData[i][2] = i; // creat threads id
LPVOID ThreadsData = (LPVOID)(&ThreadData[i]);
t[i] = CreateThread(0, 0, thread_fun, ThreadsData, 0, NULL);
if (t[i] == NULL)return 0;
}
while (1) {
DWORD res = WaitForMultipleObjects(data[0], t, true, 1000);
if (res != WAIT_TIMEOUT) break;
}
for (int i = 0; i < data[0]; i++)CloseHandle(t[i]); // close all threads
CloseHandle(event); // close event
CloseHandle(mutex); //close mutex
printf("Done");
}
主要思想是等到除一个线程之外的所有线程都到达事件并在那里等待,同时最后一个线程必须将它们从等待中释放。
但是代码不能可靠地工作。 10次中有1次正确结束,9次正好卡在while(1)
。在不同的尝试中,printf
in while
(printf("Run:%d\n", runner);
) 打印出不同数量的跑步者(0 和 3)。
可能是什么问题?
正如我们在评论部分发现的那样,问题在于尽管事件是在未发出信号的初始状态下创建的
event = CreateEvent(NULL, TRUE, FALSE, NULL);
之后立即设置为信号状态:
SetEvent(event);
因此,至少在循环的第一次迭代中,当 j == 0
时,第一个工作线程不会等待第二个工作线程,这导致 race condition。
此外,您的代码存在以下问题值得一提(尽管这些问题不是您出现问题的原因):
- 按照Microsoft documentation on
PulseEvent
, that function should not be used, as it can be unreliable and is mainly provided for backward-compatibility. According to the documentation, you should use condition variables代替。
- 在您的函数
thread_fun
中,最后一个线程在循环中锁定和释放互斥锁。这可能很糟糕,因为不能保证互斥量是公平的,并且这可能会导致其他线程永远无法获取互斥量。尽管通过在每个循环迭代中调用一次 Sleep(10);
可以减轻这种可能性,但它仍然不是理想的解决方案。更好的解决方案是使用条件变量,以便线程仅在另一个线程实际发出可能的更改信号时才检查变量 runner
的更改。出于性能原因,这样的解决方案也会更好。
我的代码有问题:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <windows.h>
#include <string.h>
#include <math.h>
HANDLE event;
HANDLE mutex;
int runner = 0;
DWORD WINAPI thread_fun(LPVOID lpParam) {
int* data = (int*)lpParam;
for (int j = 0; j < 4; j++) { //this loop necessary in order to reproduce the issue
if ((data[2] + 1) == data[0]) { // if it is last thread
while (1) {
WaitForSingleObject(mutex, INFINITE);
if (runner == data[0] - 1) { // if all other thread reach event break
ReleaseMutex(mutex);
break;
}
printf("Run:%d\n", runner);
ReleaseMutex(mutex);
Sleep(10);
}
printf("Check Done:<<%d>>\n", data[2]);
runner = 0;
PulseEvent(event); // let all other threads continue
}
else { // if it is not last thread
WaitForSingleObject(mutex, INFINITE);
runner++;
ReleaseMutex(mutex);
printf("Wait:<<%d>>\n", data[2]);
WaitForSingleObject(event, INFINITE); // wait till all other threads reach this stage
printf("Exit:<<%d>>\n", data[2]);
}
}
return 0;
}
int main()
{
event = CreateEvent(NULL, TRUE, FALSE, NULL);
mutex = CreateMutex(NULL, FALSE, NULL);
SetEvent(event);
int data[3] = {2,8}; //0 amount of threads //1 amount of numbers
HANDLE t[10000];
int ThreadData[1000][3];
for (int i = 0; i < data[0]; i++) {
memcpy(ThreadData[i], data, sizeof(int) * 2); // copy amount of threads and amount of numbers to the threads data
ThreadData[i][2] = i; // creat threads id
LPVOID ThreadsData = (LPVOID)(&ThreadData[i]);
t[i] = CreateThread(0, 0, thread_fun, ThreadsData, 0, NULL);
if (t[i] == NULL)return 0;
}
while (1) {
DWORD res = WaitForMultipleObjects(data[0], t, true, 1000);
if (res != WAIT_TIMEOUT) break;
}
for (int i = 0; i < data[0]; i++)CloseHandle(t[i]); // close all threads
CloseHandle(event); // close event
CloseHandle(mutex); //close mutex
printf("Done");
}
主要思想是等到除一个线程之外的所有线程都到达事件并在那里等待,同时最后一个线程必须将它们从等待中释放。
但是代码不能可靠地工作。 10次中有1次正确结束,9次正好卡在while(1)
。在不同的尝试中,printf
in while
(printf("Run:%d\n", runner);
) 打印出不同数量的跑步者(0 和 3)。
可能是什么问题?
正如我们在评论部分发现的那样,问题在于尽管事件是在未发出信号的初始状态下创建的
event = CreateEvent(NULL, TRUE, FALSE, NULL);
之后立即设置为信号状态:
SetEvent(event);
因此,至少在循环的第一次迭代中,当 j == 0
时,第一个工作线程不会等待第二个工作线程,这导致 race condition。
此外,您的代码存在以下问题值得一提(尽管这些问题不是您出现问题的原因):
- 按照Microsoft documentation on
PulseEvent
, that function should not be used, as it can be unreliable and is mainly provided for backward-compatibility. According to the documentation, you should use condition variables代替。 - 在您的函数
thread_fun
中,最后一个线程在循环中锁定和释放互斥锁。这可能很糟糕,因为不能保证互斥量是公平的,并且这可能会导致其他线程永远无法获取互斥量。尽管通过在每个循环迭代中调用一次Sleep(10);
可以减轻这种可能性,但它仍然不是理想的解决方案。更好的解决方案是使用条件变量,以便线程仅在另一个线程实际发出可能的更改信号时才检查变量runner
的更改。出于性能原因,这样的解决方案也会更好。