SetEvent和WaitForMultipleObjects的不可预测行为

SetEvent和WaitForMultipleObjects的不可预测行为,c,winapi,synchronization,C,Winapi,Synchronization,我试图使用SetEvent和WaitForMultipleObjects因此我编写了两个函数,希望使用事件同步这些函数。我希望第一个函数执行,然后向第二个函数发出执行信号,这反过来又向第一个函数发出再次执行的信号,等等 创建了两个事件,一个初始化为有信号状态,另一个为无信号状态。我能让执行按预期进行的唯一方法是将每个线程休眠10毫秒。我阅读了有关使用SetEvent的一些注意事项: 我的问题是,我必须让线程休眠,因为这些函数调用需要额外的时间来实际发出事件信号?调试时,有时设置的事件仍保持未调

我试图使用
SetEvent
WaitForMultipleObjects
因此我编写了两个函数,希望使用事件同步这些函数。我希望第一个函数执行,然后向第二个函数发出执行信号,这反过来又向第一个函数发出再次执行的信号,等等

创建了两个事件,一个初始化为有信号状态,另一个为无信号状态。我能让执行按预期进行的唯一方法是将每个线程休眠10毫秒。我阅读了有关使用SetEvent的一些注意事项:

我的问题是,我必须让线程休眠,因为这些函数调用需要额外的时间来实际发出事件信号?调试时,有时设置的事件仍保持未调用状态。我使用process explorer对此进行验证。我的代码基本上如下所示:

大体上:

//1. FinCalcpidUpdatestruct <<  initialize to unsignalled
//2. FinUpdatestructCalcpid << initialize to signalled
FinCalcpidUpdatestruct = CreateEvent (NULL, FALSE, FALSE, TEXT("FinCalcpidUpdatestruct"));
FinUpdatestructCalcpid = CreateEvent (NULL, FALSE, TRUE, TEXT ("FinUpdatestructCalcpid"));


void function1()
{
    int n;
    int noGoCode;
    n=0;

    noGoCode = 0;
    while(1)
    {
    //Sleep(10);
    noGoCode = WaitForSingleObject (FinCalcpidUpdatestruct, INFINITE);
    if (noGoCode == WAIT_OBJECT_0) {

    //wait for FinCalcpidUpdatestruct to be signalled
    BufferOut[n] = pid_data_array -> output;

    //signal FinUpdatestructCalcpid 
    if(!SetEvent (FinUpdatestructCalcpid))
        printf("Couldn't set the event FinUpdatestructCalcpid\n");
    else{
        printf("FinUpdatestructCalcpid event set\n");
        Sleep(10);
        }
    }
    else
        printf("error\n");
    }
}

void function2()
{
    int nGoCode = 0;
    while(1)
    {
    //wait for FinUpdatestructCalcpid to be signalled

    // Sleep(10);
    nGoCode = WaitForSingleObject (FinUpdatestructCalcpid, INFINITE);
    if (nGoCode == WAIT_OBJECT_0) {
    if(!SetEvent (FinCalcpidUpdatestruct))
        printf("Couldn't set the event FinCalcpidUpdatestruct\n");
    else{
        printf("FinCalcpidUpdatestruct event set\n");
        Sleep(10);
        }
    }
    else
        printf("error\n");
    }//end while(1)
//1。FinCalcpidUpdatestruct以下是我的想法

在设置事件之前,不会输出任何状态。此时,执行可以在输出任何内容之前切换到另一个线程。如果该线程先运行,那么它可能看起来就像一个线程的循环已经运行了第二次--一个没有乒乓球的ping

但是如果在接收信号和设置事件之间增加一个全局计数器,然后将该计数器的值保存到一个局部变量,您可能会发现一个线程的计数总是奇数,而另一个线程的计数总是偶数(这是您想要的行为),即使输出不是很有序。

我想

在设置事件之前,不会输出任何状态。此时,执行可以在输出任何内容之前切换到另一个线程。如果该线程先运行,那么它可能看起来就像一个线程的循环已经运行了第二次--一个没有乒乓球的ping


但是如果在接收信号和设置事件之间增加一个全局计数器,然后将该计数器的值保存到一个局部变量,您可能会发现一个线程的计数总是奇数,而另一个线程的计数总是偶数(这是您想要的行为),即使输出不是很有序。

您也可以通过在每个函数的
SetEvent
调用上方添加
printf
来解决问题

问题是您正在设置事件,然后执行一些输出

function2
中,
printf
发生在
SetEvent
之后:

// Add a printf call here to see sensible output.
if(!SetEvent (FinUpdatestructCalcpid))
    printf("Couldn't set the event FinUpdatestructCalcpid\n");
else{
    // Thread is pre-empted by kernel here.  This is not executed immediately
    printf("FinUpdatestructCalcpid event set\n");
}
内核支持运行
function2
的线程,因此
FinUpdatestructCalcpid
事件现在已设置,而没有您期望的相应
printf

然后执行运行
function1
的线程,并设置
FinUpdatestructCalcpid
事件。运行
function2
的线程现在可以执行,并从停止的位置继续执行。它运行
printf
,由于设置了
FinUpdatestructCalcpid
事件,因此它立即再次运行


Sleep()
调用您正在使用的帮助,使此竞态条件不太可能发生,但不要消除它。

您可以通过在每个函数的
SetEvent
调用上方添加
printf
来解决此问题

问题是您正在设置事件,然后执行一些输出

function2
中,
printf
发生在
SetEvent
之后:

// Add a printf call here to see sensible output.
if(!SetEvent (FinUpdatestructCalcpid))
    printf("Couldn't set the event FinUpdatestructCalcpid\n");
else{
    // Thread is pre-empted by kernel here.  This is not executed immediately
    printf("FinUpdatestructCalcpid event set\n");
}
内核支持运行
function2
的线程,因此
FinUpdatestructCalcpid
事件现在已设置,而没有您期望的相应
printf

然后执行运行
function1
的线程,并设置
FinUpdatestructCalcpid
事件。运行
function2
的线程现在可以执行,并从停止的位置继续执行。它运行
printf
,由于设置了
FinUpdatestructCalcpid
事件,因此它立即再次运行


Sleep()
调用您正在使用的帮助,以使此竞态条件不太可能发生,但不要消除它。

为了简洁起见,首先让我将代码缩短:

FinCalcpidUpdatestruct = CreateEvent (NULL, FALSE, FALSE, TEXT("FinCalcpidUpdatestruct"));
FinUpdatestructCalcpid = CreateEvent (NULL, FALSE, TRUE, TEXT ("FinUpdatestructCalcpid"));

void function1()
{
    while(1)
    {
        WaitForSingleObject (FinCalcpidUpdatestruct, INFINITE);
        // Do Something
        SetEvent (FinUpdatestructCalcpid);
        printf(...);
        Sleep(10);
    }
}

void function2()
{
    while(1)
    {
        nGoCode = WaitForSingleObject (FinUpdatestructCalcpid, INFINITE);
        SetEvent (FinCalcpidUpdatestruct);
        printf(...); // **A**
        Sleep(10);
    }
}
基本上,在执行的任何一点上,该控件都可以从线程中移除,并交给另一个线程。现在假设
function2
已经设置了事件,并且即将在代码中打印
**A**
的输出。在打印输出之前,控件被取下并交给
功能1
。最后一次打印的输出已经来自
function1
,并且它的等待事件已设置,因此它将失败并再次打印其内容

发生这种情况时,您的输出会偶尔出现一次:

function1
function2
function1
function2
function1
// When the situation **A** above happens:
function1
function2
function2
function1
// And we move on as usual further
function2
function1
function2

完成后,即在
printf
之后设置事件,您会没事的。

为了简洁起见,让我先把代码缩短:

FinCalcpidUpdatestruct = CreateEvent (NULL, FALSE, FALSE, TEXT("FinCalcpidUpdatestruct"));
FinUpdatestructCalcpid = CreateEvent (NULL, FALSE, TRUE, TEXT ("FinUpdatestructCalcpid"));

void function1()
{
    while(1)
    {
        WaitForSingleObject (FinCalcpidUpdatestruct, INFINITE);
        // Do Something
        SetEvent (FinUpdatestructCalcpid);
        printf(...);
        Sleep(10);
    }
}

void function2()
{
    while(1)
    {
        nGoCode = WaitForSingleObject (FinUpdatestructCalcpid, INFINITE);
        SetEvent (FinCalcpidUpdatestruct);
        printf(...); // **A**
        Sleep(10);
    }
}
基本上,在执行的任何一点上,该控件都可以从线程中移除,并交给另一个线程。现在假设
function2
已经设置了事件,并且即将在代码中打印
**A**
的输出。在打印输出之前,控件被取下并交给
功能1
。最后一次打印的输出已经来自
function1
,并且它的等待事件已设置,因此它将失败并再次打印其内容

发生这种情况时,您的输出会偶尔出现一次:

function1
function2
function1
function2
function1
// When the situation **A** above happens:
function1
function2
function2
function1
// And we move on as usual further
function2
function1
function2

完成后,即在
printf
之后设置事件,您就会没事。

缓冲IO代替线程上下文切换是一个残忍无情的婊子。从你的代码中,你是说SetEvent失败了吗?好吧,如果stdout到达终端,它应该是行缓冲的@matrixelk,有重定向吗?另外,看到真实的代码(编译的代码)也很好:这里可能丢失了一些重要的东西。自动重置事件可能会出现时间问题-您是否尝试过手动重置事件并