Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Критические секции (CRITICAL_SECTION)


Автор: neokoder 11.3.2011, 10:17
Привет коллеги.

Вот у Рихтера прочитал, что иногда InitializeCriticalSection и EnterCriticalSection могут вызывать исключения, очень редко, но всё же могут. Поэтому решил написать 2 универсальные функции инициализации и входа в критическую секцию.

Если у меня есть какие-то "жучки" в коде был бы рад если вы мне на них указали, особенно это касается использования goto, не возникнет ли каких-либо проблем после такого использования. 


Код

#define TRY_INIT_CS_COUNT 10
#define TRY_ENTER_CS_COUNT 10


UINT8 my_InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount)
{
 int i,j;

  j=0; 

again1:
  try
  {
      for (i=0;i<TRY_INIT_CS_COUNT;i++)
      {
         if (InitializeCriticalSectionAndSpinCount(lpCriticalSection, dwSpinCount)) return 1;
         SwitchToThread();
      }

  }
  catch(...)
  { 
   j++;
   if (j>=TRY_INIT_CS_COUNT) return 0;
   SwitchToThread();
   goto again1;
  }

  return 0;
}


UINT8 my_TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
{
  int i,j;

  j=0; 

again2:
  try
  {
      for (i=0;i<TRY_ENTER_CS_COUNT;i++)
      {
         if (TryEnterCriticalSection(lpCriticalSection)) return 1;
         SwitchToThread();
      }

  }
  catch(...)
  { 
   j++;
   if (j>=TRY_ENTER_CS_COUNT) return 0;
   SwitchToThread();
   goto again2;
  }

}


А использовать примерно так:
Код

CRITICAL_SECTION cs1;

//use of my_InitializeCriticalSectionAndSpinCount
if (!my_InitializeCriticalSectionAndSpinCount(&cs1,1000))
{
 printf("Error: can't initialize critical section");
 return 1;
}


//use of my_TryEnterCriticalSection
if (!my_TryEnterCriticalSection(&cs1))
{
  printf("Error: can't enter critical section");
  return 1;
}
else
{
   __try
   {
     //some code
     //...
   }
    __finally
   {
     LeaveCriticalSection(&cs1);
   }
}


Автор: Estranged 12.3.2011, 13:32
Внимательно читайте документацию. Английским по белому написано про эти исключения: http://msdn.microsoft.com/en-us/library/ms682608%28v=vs.85%29.aspx.

This function can raise EXCEPTION_POSSIBLE_DEADLOCK if a wait operation on the critical section times out. The timeout interval is specified by the following registry value: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\CriticalSectionTimeout. Do not handle a possible deadlock exception; instead, debug the application.

Windows 2000:  In low memory situations, EnterCriticalSection can raise an exception. Do not attempt to handle this exception; instead, either terminate the process or allow the exception to pass to the unhandled exception filter. To avoid an exception due to low memory, call the InitializeCriticalSectionAndSpinCount function to preallocate the event used by EnterCriticalSection instead of calling the InitializeCriticalSection function, which forces EnterCriticalSection to allocate the event. Preallocating the event is not necessary on Windows XP or later because EnterCriticalSection is guaranteed not to fail due to lack of resources.

Старина Рихтер хорош, несомненно, но иногда данные там не соответствуют нынешним реалиям.

А второе, теперь разве плюсовые catch ловят SEH исключения от windows? VS 6 точно ловила, а вот 2008 уже нет smile

Автор: neokoder 12.3.2011, 14:01
Цитата(Estranged @  12.3.2011,  13:32 Найти цитируемый пост)
Внимательно читайте документацию. Английским по белому написано про эти исключения: MSDN.This function can raise EXCEPTION_POSSIBLE_DEADLOCK if a wait operation on the critical section times out. The timeout interval is specified by the following registry value: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\CriticalSectionTimeout. Do not handle a possible deadlock exception; instead, debug the application.Windows 2000:  In low memory situations, EnterCriticalSection can raise an exception. Do not attempt to handle this exception; instead, either terminate the process or allow the exception to pass to the unhandled exception filter. To avoid an exception due to low memory, call the InitializeCriticalSectionAndSpinCount function to preallocate the event used by EnterCriticalSection instead of calling the InitializeCriticalSection function, which forces EnterCriticalSection to allocate the event. Preallocating the event is not necessary on Windows XP or later because EnterCriticalSection is guaranteed not to fail due to lack of resources.


Где вы увидели в моём коде EnterCriticalSection? Внимательно читайте посты. 


Цитата(Estranged @  12.3.2011,  13:32 Найти цитируемый пост)
А второе, теперь разве плюсовые catch ловят SEH исключения от windows? VS 6 точно ловила, а вот 2008 уже нет

Ловят, с ключом компилятора /EHa.

Добавлено через 1 минуту и 6 секунд
Цитата(Estranged @  12.3.2011,  13:32 Найти цитируемый пост)
Старина Рихтер хорош, несомненно, но иногда данные там не соответствуют нынешним реалиям.


Всё там соответствует нынешним реалиям. Читать надо внимательнее.

Автор: Estranged 12.3.2011, 14:31
Про ключи компилятора я как-то и не подумал.
А что будет, если все 10 попыток TryEnterCriticalSection неудачны? Это типа как ошибка (printf("Error: can't enter critical section");) или все же просто кто-то слишком надолго занял секцию, надо всего лишь чуть-чуть подождать?

Автор: neokoder 13.3.2011, 21:33
В общем сошлись на другом форуме, что лучше всё же использовать классический вариант.

Проверил работу пары потоков. Если первый поток зависает с завладением критической секции, а второй входит в цикл ожидания освобождения этой крит. секции. Всё -труба! Даже если сделать TerminateThread, а потом DeleteCriticalSection и InitializeCriticalSection всё равно второй поток остаётся висеть. Уже видимо в ожидании непонятно чего.

Хотя я считаю Microsoft могли бы предусмотреть для критических секций такую же штуку как и для мьютекса, т.е. если поток завершается через TerminateThread, то критическая секция должна быть освобождена. Или вообще добавить спец. функцию, позволяющую принудительно освободить критическую секцию.

Тогда остаётся действительно только одно - заключать в критическую секцию только код, который гарантированно не зависнет. А я хотел было написать что-то универсальное, для любого случая... В общем тогда классика - наилучший вариантом будет:
Код
   __try
   {
     EnterCriticalSection(&cs1);
     //some code
   }
    __finally
   {
     LeaveCriticalSection(&cs1);
   }


И InitializeCriticalSectionAndSpinCount вызывать так:
Код
InitializeCriticalSectionAndSpinCount(&cs1,0x80000000|dwSpinCount);

В итоге получим снижение производительности только для Windows 2000, зато упростим себе задачу программирования крит. секций.

Добавлено через 42 секунды
Решено.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)