使用SetEvent和WaitForMultipleObjects的不可预知的行为

时间:2013-01-11 01:08:17

标签: c winapi synchronization

我正在尝试使用SetEventWaitForMultipleObjects,因此我编写了一些我希望使用Events进行同步的函数。我希望第一个函数执行,然后发出第二个函数执行信号,这反过来表示第一个函数再次执行等。

创建了两个事件,一个初始化为信号状态,另一个未信号。我可以让执行按预期流动的唯一方法是让每个线程休眠10毫秒。我读到了一些关于使用SetEvent的注意事项: http://cboard.cprogramming.com/windows-programming/100818-setevent-resetevent-3.html

我的问题是我必须让我的线程进入睡眠状态,因为这些函数调用需要额外的时间来实际发出事件信号吗?在调试时,有时设置的事件仍然是无信号的。我使用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)

如果没有使用睡眠,那么有时相同的功能将在while循环中运行几次,而不是在两个函数之间来回ping。有什么想法吗?

3 个答案:

答案 0 :(得分:3)

您可以在每项功能的printf电话上方添加SetEvent来解决问题。

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

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");
}

内核preempts正在运行function2的线程,因此现在已设置FinUpdatestructCalcpid事件,而没有您期望的相应printf

然后执行运行function1的线程并设置FinUpdatestructCalcpid事件。现在允许运行function2的线程执行并从它停止的位置继续。它运行printf,因为FinUpdatestructCalcpid事件已设置,它会立即再次运行。

Sleep()调用你正在使用帮助来使这种竞争条件不太可能,但不要消除它。

答案 1 :(得分:1)

首先让我简短地删除您的代码:

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**周围的输出。在打印输出之前,控件被取走并提供给function1。最后打印的输出已经来自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之后,您就可以了。

答案 2 :(得分:0)

这就是我的想法......

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

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