В статических переменных в области видимости возникает тупик (небезопасный поток в VC ++) - PullRequest
3 голосов
/ 26 февраля 2010

Вопрос в том, как строится статика на уровне функций, когда функция вызывается в нескольких потоках?

Описание проблемы: возникает тупик, и мое приложение не закрывается. Во время инициализации локальной статической переменной он пытается получить MSVCR80! _Lock и никогда не захватывает блокировку.

Команда

! Locks в WinDbg дает следующий вывод.

CritSec ntdll! LdrpLoaderLock + 0 при 7c97e178
LockCount 0

RecursionCount 1

OwningThread 1998

EntryCount d

ContentionCount d

*** Заблокировано

CritSec MSVCR80! __ app_type + 94 при 781c3bc8

LockCount 1

RecursionCount 1

OwningThread 9a8

EntryCount 1

ContentionCount 1

*** Заблокировано

Ниже приведен стек вызовов, и вы увидите, что он никогда не захватит блокировку _mlock

#

**

Стек вызовов Thread 17e8

**

781c3bc8 78132bd9 0777fde4 ntdll! RtlEnterCriticalSection + 0x46

00000008 b87d2630 00000000 MSVCR80! _Lock + 0x2e

0864ae10 08631d7f 0864ae10 EPComUtilities32! _Onexit + 0x36

0864ae10 b87d2588 00000001 EPComUtilities32! Atexit + 0x9

0777fea8 0864719f 08630000 EPComUtilities32! XCriticalSectionEx :: ThreadTermination + 0x5f

08630000 00000003 00000000 EPComUtilities32! DllMain + 0x20

08630000 7c90118a 08630000 EPComUtilities32! __DllMainCRTStartup + 0x7a

08630000 00000003 00000000 EPComUtilities32! _DllMainCRTStartup + 0x1d #

**

вызов стека потока 1100

**

000000b0 00000000 00000000 ntdll! ZwWaitForSingleObject + 0xc

000000b0 ffffffff 00000000 kernel32! WaitForSingleObjectEx + 0xa8

000000b0 ffffffff 06ce64e0 kernel32! WaitForSingleObject + 0x12

000480ba 000f4240 00000000 CATSysMultiThreading! CATThreads :: Join + 0xf5

0012fcc8 00000004 00000000 JS0GROUP! CATLM :: StopHB + 0xf4

d138509f 00416694 00000001 JS0GROUP! CATLM :: Unmake + 0x6b

00000000 00000000 00000000 MSVCR80! _Cinit + 0xd6

00000000 0012fd6c 081e68d9 MSVCR80! Выход + 0xd

00000000 06d404f0 0998fb90 JS0GROUP! CATExit + 0x1d

00000000 004ef366 0000000d DNBPLMProvider! DNBEPLMTransactionMgt :: OnApplicationExit + 0x229

00000000 0012fd9c 004eabfc JS0GROUP! CATCallExits + 0x2bc

00000000 0012ff7c 0040cefd JS0GROUP! CATErrorNormalEnd + 0x31 00000000 06ce71d0 06ce71d0 JS0GROUP! CATExit + 0xc

00000007 06cdb120 059b61d8 DLMMfgContextSolver! Main + 0x146d

ffffffff ffffffff bffde000 DLMMfgContextSolver! __TmainCRTStartup + 0x10f

// Code snippet below 
void main() 
{

    atexit(MyCallBack); 
    exit(0); 

}

void MyCallBack() 
{

// Waitingforsingleobject() // Waits until all threads are terminated

}

EXE вызывает DllMain с флагом DLL_THREAD_DETACH, и мы имеем явную обработку, как показано ниже

BOOL APIENTRY DllMain( HANDLE, DWORD dwReason, LPVOID ) 
{
  if(dwReason == DLL_THREAD_DETACH) 
  { 
    F1();
    F2();
  }
}

F1()
{

    const static CComBSTR bstrMethod = __ FUNCTION __ ;

}

F2()
{

    const static CComBSTR bstrMethod = __ FUNCTION __ ;

}

Потоково ли безопасно иметь локальную статическую инициализацию внутри функции. Также я заметил, что если статическая переменная однажды инициализируется перед выходом () основного приложения, я не вижу никаких проблем. Кто-нибудь может объяснить, в чем может быть проблема?

Примечание: Но когда я делаю статическую переменную не статической, тупик не возникает, и проблема решается.

Также дайте мне знать любое альтернативное решение, которое может помочь в этой ситуации. С нетерпением жду ответа.

Ответы [ 3 ]

1 голос
/ 04 марта 2010

В некотором смысле, на этот вопрос ответили комментарии Влада к вашему вопросу: ничего не делать (если это вообще возможно) в DllMain.

Вы действительно ограничены тем, что разумно в DllMain. Все, что связано с копанием в другом Dll (включая использование malloc и т. Д.), Отсутствует. Фактически, единственные вещи, которые вы можете сделать, это: делать вызовы в Kernel32.dll (кроме загрузки / выгрузки Dlls!) И инициализировать простые типы данных.

Я действительно думаю, что, учитывая ваше описание проблемы, использование CComBSTR нарушает одно или несколько из этих правил.

Надеюсь, это поможет,

приписка

Дополнительная ссылка из блогов MSDN: http://blogs.msdn.com/oleglv/default.aspx

0 голосов
/ 01 марта 2010

Я попробую, хотя я не знаком с классом CComBSTR.

По сути, у вас есть один объект, который используется (в данном случае созданный) несколькими потоками без защиты от параллелизма. Разве это не похоже на плохую вещь?

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

0 голосов
/ 01 марта 2010

Можете ли вы установить точку останова в C:\Program Files\Microsoft Visual Studio 8\VC\crt\src\atonexit.c в строке 103 и записать, какие потоки получают _EXIT_LOCK1 в каком порядке (и каков стек вызовов в каждом случае и, возможно, какие другие блокировки уже были получены в тот момент)?

Кроме того, в вашем примере кода Waitingforsingleobject закомментировано - не могли бы вы подтвердить, что оно действительно закомментировано в вашем коде, когда возникает тупик?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...