Windows 7中的临界区问题

时间:2010-10-21 23:08:46

标签: c++ winapi console critical-section

为什么下面的代码示例导致一个线程执行的方式多于另一个,但是互斥锁不会?

#include <windows.h>
#include <conio.h>
#include <process.h>
#include <iostream>
using namespace std;

typedef struct _THREAD_INFO_ {

    COORD coord;        // a structure containing x and y coordinates
    INT threadNumber;   // each thread has it's own number
    INT count; 

}THREAD_INFO, * PTHREAD_INFO;

void gotoxy(int x, int y);

BOOL g_bRun; 
CRITICAL_SECTION g_cs; 

unsigned __stdcall ThreadFunc( void* pArguments )
{
    PTHREAD_INFO info = (PTHREAD_INFO)pArguments;

    while(g_bRun)
    {

        EnterCriticalSection(&g_cs); 

        //if(TryEnterCriticalSection(&g_cs))
        //{
            gotoxy(info->coord.X, info->coord.Y);
            cout << "T" << info->threadNumber << ": " << info->count;

            info->count++; 

            LeaveCriticalSection(&g_cs); 

        //}
    }

    ExitThread(0);
    return 0;
}

int main(void)
{
    // OR unsigned int
    unsigned int id0, id1; // a place to store the thread ID returned from CreateThread
    HANDLE h0, h1;  // handles to theads

    THREAD_INFO tInfo[2]; // only one of these - not optimal!

    g_bRun = TRUE;

    ZeroMemory(&tInfo, sizeof(tInfo)); // win32 function - memset(&buffer, 0, sizeof(buffer))

    InitializeCriticalSection(&g_cs); 

    // setup data for the first thread
    tInfo[0].threadNumber = 1;
    tInfo[0].coord.X = 0;
    tInfo[0].coord.Y = 0;

    h0 = (HANDLE)_beginthreadex( 
            NULL,        // no security attributes
            0,           // defaut stack size
            &ThreadFunc, // pointer to function
            &tInfo[0],   // each thread gets its own data to output
            0,           // 0 for running or CREATE_SUSPENDED 
            &id0 ); // return thread id - reused here

    // setup data for the second thread
    tInfo[1].threadNumber = 2;
    tInfo[1].coord.X = 15;
    tInfo[1].coord.Y = 0;

    h1 = (HANDLE)_beginthreadex( 
            NULL,        // no security attributes
            0,           // defaut stack size
            &ThreadFunc, // pointer to function
            &tInfo[1],   // each thread gets its own data to output
            0,           // 0 for running or CREATE_SUSPENDED 
            &id1 ); // return thread id - reused here

    _getch(); 

    g_bRun = FALSE; 

    return 0;
}

void gotoxy(int x, int y)   // x=column position and y=row position
{
   HANDLE hdl;
   COORD coords;
   hdl = GetStdHandle(STD_OUTPUT_HANDLE);
   coords.X = x;
   coords.Y = y;      
   SetConsoleCursorPosition(hdl, coords);
}

5 个答案:

答案 0 :(得分:3)

这可能无法回答您的问题,但关键部分的行为在Windows Server 2003 SP1及更高版本上发生了更改。

如果您有与Windows 7关键部分相关的错误,您无法在XP计算机上重现,则可能会受到该更改的影响。

我的理解是,在Windows XP上,关键部分使用基于FIFO的策略,这对所有线程都是公平的,而后期版本使用旨在减少线程之间上下文切换的新策略。

MSDN page about critical sections

上有关于此的简短说明

您可能还想查看this forum post

答案 1 :(得分:2)

关键部分(如互斥锁)旨在保护共享资源免受冲突访问(例如并发修改)。关键部分意味着替换线程优先级。

您已经人为地引入了共享资源(屏幕)并使其成为瓶颈。因此,关键部分具有很强的竞争力。由于两个线程具有相同的优先级,因此Windows不会选择一个线程而不是另一个线程。减少上下文切换选择一个线程而不是另一个线程的原因。由于这种减少,共享资源的利用率上升。这是一件好事;这意味着一个线程将在之前完成 lot ,而另一个线程将提前完成。

要以图形方式查看效果,请比较

A B A B A B A B A B

AAAAA BBBBB

第二个序列较短,因为从A到B只有一个开关。

答案 2 :(得分:0)

手边浪漫的术语:

CriticalSection说线程希望控件一起做一些事情。

Mutex正在制作一个标记来显示“忙碌”,以便其他人可以等待并通知完成,以便其他人可以启动。其他人已经在等待互斥锁会抓住它,然后再次启动循环并将其恢复。

因此,使用CriticalSection得到的结果是在循环之间无法产生。如果您在Sleep(0);

之后LeaveCriticalSection,则可能会有所不同

答案 3 :(得分:0)

我不能说你为什么要观察这种特殊行为,但这可能与每种机制的实现细节有关。我可以说的是,解锁然后立即锁定互斥锁是一件坏事。你最终会观察到奇怪的行为。

答案 4 :(得分:0)

来自某些MSDN文档(http://msdn.microsoft.com/en-us/library/ms682530.aspx):

  

从带有Service Pack 1(SP1)的Windows Server 2003开始,等待关键部分的线程不会按先到先得的原则获取关键部分。对于大多数代码而言,此更改会显着提高性能