проблема критической секции в Windows 7 - PullRequest
2 голосов
/ 22 октября 2010

Почему приведенный ниже пример кода заставляет один поток выполняться быстрее, чем другой, а мьютекс - нет?

#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 ]

3 голосов
/ 22 октября 2010

Это может не ответить на ваш вопрос, но поведение критических разделов изменилось в Windows Server 2003 SP1 и более поздних версиях.

Если у вас есть ошибки, связанные с критическими разделами в Windows 7, которые вы не можете воспроизвести на компьютере с XP, это изменение может повлиять на вас.

Насколько я понимаю, в критических разделах Windows XP использовалась стратегия на основе FIFO, которая была справедливой для всех потоков, а в более поздних версиях использовалась новая стратегия, направленная на уменьшение переключения контекста между потоками.

На странице MSDN есть краткое примечание о критических разделах

Вы также можете проверить это сообщение на форуме

2 голосов
/ 22 октября 2010

Критические разделы, такие как мьютексы, предназначены для защиты общего ресурса от конфликтующего доступа (например, одновременного изменения). Критические разделы не предназначены для замены приоритета потока.

Вы искусственно представили общий ресурс (экран) и превратили его в узкое место. В результате критическая секция очень спорна. Поскольку оба потока имеют одинаковый приоритет, для Windows нет причин предпочитать один поток другому. Сокращение контекстных переключателей является причиной выбора одного потока над другим. В результате этого сокращения использование общего ресурса увеличивается. Это хорошая вещь; это означает, что один поток будет завершен на лот раньше, а другой поток - немного раньше.

Чтобы увидеть эффект графически, сравните

A B A B A B A B A B

до

AAAAA BBBBB

Вторая последовательность короче, потому что есть только один переключатель с A на B.

0 голосов
/ 22 октября 2010

Из некоторых документов MSDN (http://msdn.microsoft.com/en-us/library/ms682530.aspx):

Начиная с Windows Server 2003 с пакетом обновления 1 (SP1), потоки, ожидающие в критическом разделе, не получают критический раздел в порядке поступления, в первую очередь. Это изменение значительно повышает производительность для большинства кодов

0 голосов
/ 22 октября 2010

Я не могу сказать, почему вы наблюдаете именно это поведение, но это, вероятно, связано со спецификой реализации каждого механизма. Я могу сказать, что разблокировка и немедленная блокировка мьютекса - это плохо. Со временем вы увидите странное поведение.

0 голосов
/ 22 октября 2010

В руках волнистые термины:

CriticalSection говорит, что поток хочет, чтобы управление делало некоторые вещи вместе.

Mutex делает маркер, показывающий «занят», чтобы другие могли ждать и уведомлять о завершении, чтобы кто-то еще мог начать. Кто-то, кто уже ждет мьютекса, захватит его, прежде чем вы сможете снова запустить цикл и вернуть его обратно.

Итак, что вы получаете с CriticalSection - это неспособность уступать между циклами. Вы можете увидеть разницу, если у вас было Sleep(0); после LeaveCriticalSection

...