【问题标题】:critical section problem in Windows 7Windows 7 中的临界区问题
【发布时间】:2010-10-21 23:08:46
【问题描述】:

为什么下面的代码示例会导致一个线程比另一个线程执行得更多,而互斥锁却不会?

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

【问题讨论】:

    标签: c++ winapi console critical-section


    【解决方案1】:

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

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

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

    MSDN page about critical sections 上有一个简短的说明

    您可能还想查看this forum post

    【讨论】:

    • 谢谢。论坛帖子似乎回答了这个问题。结论似乎是关键部分不是共享日志文件等资源的好方法。
    • 这不是一个正确的结论。关键部分应该是完全没问题的。只要磁盘带宽高于日志文件的写入速率,最终都会为所有线程提供服务。当写入的总速率超过磁盘带宽时,您会遇到比不公平调度更大的问题。
    【解决方案2】:

    诸如互斥锁之类的关键部分旨在保护共享资源免受冲突访问(例如并发修改)。临界区意在取代线程优先级。

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

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

    A B A B A B A B A B
    

    AAAAA BBBBB
    

    第二个序列较短,因为从 A 到 B 只有一次切换。

    【讨论】:

      【解决方案3】:

      在手波术语:

      CriticalSection 是说线程想要控制一起做一些事情。

      Mutex 正在制作一个标记来显示“正在忙碌”,以便其他人可以等待并通知完成,以便其他人可以开始。已经在等待互斥锁的其他人会在您再次启动循环并将其取回之前抓住它。

      因此,您使用 CriticalSection 得到的结果是循环之间无法让步。如果您在LeaveCriticalSection 之后有Sleep(0);,您可能会看到不同

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

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

          从带有 Service Pack 1 (SP1) 的 Windows Server 2003 开始​​,在临界区等待的线程不会以先到先得的方式获取临界区。此更改显着提高了大多数代码的性能

          【讨论】:

          • 该代码在 XP 上运行良好,但在同一硬件上却不是 7。在显示的代码中,一个线程通常占主导地位,而另一个线程每隔几秒运行一次。还值得注意的是,SRW 的行为方式相同。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-06-24
          • 1970-01-01
          • 2021-04-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多