Модераторы: Snowy, bartram, MetalFan, bems, Poseidon, Riply
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Вылетает AV несмотря на использование, CriticalSection 
:(
    Опции темы
Coder
Дата 11.7.2008, 03:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 733
Регистрация: 13.12.2004

Репутация: 1
Всего: 11



Суть в следующем - пытаюсь предотвратить одновременный доступ нескольких потоков к процедуре добавления новых данных в общий лог.

Модуль ведения лога:
Код

{$I Defines.inc}

unit pv_Log;

interface

procedure ShowLogWindow;
procedure AddLogMessage(_LogMessage : PAnsiChar);

implementation

uses
  pv_vars, windows, messages, pv_GraphicsObj, sysutils;

{$I 'res_cons.inc'}

var
  LogMessage : PAnsiChar = nil;
  LogLen : Cardinal = 0;
  lCritSect : TRTLCriticalSection;

function _MessageLogProc (_hWnd : HWND; _Msg : UINT; _wParam : WPARAM; _lParam : LPARAM) : Integer; stdcall;
begin
  case _Msg of
    WM_INITDIALOG :
      begin
        SendDlgItemMessage(_hWnd, IDC_LOG, WM_SETFONT, WPARAM(gdi_hfntFixed), 0);
        SetDlgItemText (_hWnd, IDC_LOG, LogMessage);
      end;
    WM_COMMAND :
      case LoWord(_wParam) of
        IDC_CLEAR_LOG :
          begin
            FreeMem(LogMessage);
            LogMessage:=nil;
            LogLen:=0;
            SetDlgItemText (_hWnd, IDC_LOG, LogMessage);
            Result:=1;
            exit;
          end;
        ID_OK, ID_CANCEL :
          begin
            EndDialog(_hWnd,0);
            result:=1;
            exit;
          end;
      end;
  end;

  Result:=0;
end;

// "защищаемая" процедура
procedure AddLogMessage(_LogMessage : PAnsiChar);
var
  _new_size : Cardinal;
begin
{ TODO : Сделать ограничение длины лога. }
  EnterCriticalSection(lCritSect);

  if LogMessage=nil then
    begin
      LogLen:=StrLen(_LogMessage)+1;
      LogMessage:=AllocMem(LogLen);
      StrCopy(LogMessage,_LogMessage);
    end
  else
    begin
      _new_size:=LogLen+StrLen(_LogMessage)+2;
      ReallocMem(LogMessage, _new_size);
      LogLen:=_new_size;
      StrCat(LogMessage,#13#10);
      StrCat(LogMessage,_LogMessage);
    end;

  LeaveCriticalSection(lCritSect);
end;

procedure ShowLogWindow;
begin
  DialogBoxParam(hInstan, MAKEINTRESOURCE(DLG_LOG), hMainForm, @_MessageLogProc, 0);
end;

initialization
  LogMessage:=nil;
  InitializeCriticalSection(lCritSect);

finalization
  FreeMem(LogMessage);
  DeleteCriticalSection(lCritSect);

end.


Из кода видно, что процедура AddLogMessage() в конкретный момент времени должна выполнятся одним потоком.

Тестирую так:

Код

function _AddLog(lpParameter : Pointer) : DWORD; stdcall;
var
  i : Integer;
  str : array[0..50] of char;
begin
  randomize;
  for i:=1 to 5 do
    begin
      ZeroMemory(@str,sizeof(str));

      StrCopy(str,'Сообщение №');
      StrCat(str,pchar(inttostr(i)));

      AddLogMessage(str);

      sleep(random(100)); // для рассинхронизации очереди потоков
    end;
end;

for testI:=1 to 10 do
  CreateThread(nil,0,@_AddLog,nil,0,dwReadThread);


Самое интересное, что если убрать   EnterCriticalSection(lCritSect) и   LeaveCriticalSection(lCritSect), то AV вываливается при первом же запуске теста. В противном случае AV вываливается не сразу - а скажем после 4, 5 вызовов тестового цикла  (for testI:=1 to 10 do).

В чем здесь может быть дело?
PM MAIL   Вверх
Riply
Дата 11.7.2008, 05:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(Coder @  11.7.2008,  03:57 Найти цитируемый пост)
В чем здесь может быть дело? 


Неряшливо написанный код  (пусть даже временный) - основная причина  всяких "багов" и ошибок.
Приведи его в порядок:
1. Где try/finally ?
2. Где закрытие Handle`ов ?
3. Почему randomize вызывается столько раз ?

Теперь насчет структуры:

Вместо CreateThread, настоятельно рекомендую
использовать BeginThread, либо тщательно изучить и понять их отличие. 
(Там все сложнее, чем просто установка IsMultiThread)

А если все же (по религиозным соображениям  smile ) нужна именно CreateThread,
то попробуй обернуть в кр. секцию не  AddLogMessage, а _AddLog
и посмотри как измениться поведение  smile 


PM MAIL   Вверх
Coder
Дата 11.7.2008, 05:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 733
Регистрация: 13.12.2004

Репутация: 1
Всего: 11



Цитата(Riply @  11.7.2008,  13:16 Найти цитируемый пост)
Где try/finally

Я не вижу смысла расставлять их по данному коду.

Цитата(Riply @  11.7.2008,  13:16 Найти цитируемый пост)
Где закрытие Handle`ов

Здесь это тоже не критично важно.

Цитата(Riply @  11.7.2008,  13:16 Найти цитируемый пост)
Почему randomize вызывается столько раз

Согласен - нужно вынести его из кода потоковой функции.

Цитата(Riply @  11.7.2008,  13:16 Найти цитируемый пост)
,...то попробуй обернуть в кр. секцию не  AddLogMessage, а _AddLogи посмотри как измениться поведение   

Так не пойдет - все потоки будут ждать пока 1 не добавит все 5 сообщений. Тем более что _AddLog тестовая процедура, на практике все потоки пользуются AddLogMessage.

PM MAIL   Вверх
Riply
Дата 11.7.2008, 08:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(Coder @  11.7.2008,  05:51 Найти цитируемый пост)
Цитата(Riply @  11.7.2008,  13:16 )
Где try/finally


Я не вижу смысла расставлять их по данному коду.


Цитата(Riply @  11.7.2008,  13:16 )
Где закрытие Handle`ов


Здесь это тоже не критично важно.


Тогда, я - пас, ибо для меня это критично.
PM MAIL   Вверх
Coder
Дата 11.7.2008, 09:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 733
Регистрация: 13.12.2004

Репутация: 1
Всего: 11



Riply, ну хорошо, handler`ы закрою . Что изменится? Мне не понятно, почему программа падает, при "интенсивном" обращении  к процедуре AddLogMessage. Всего-то smile


Riply, Все-таки это тестовый пример для тестирования главной процедуры, и не важно в каком состоянии будет поток после выполнения кода, главное чтобы он N число раз выполнил AddLogMessage.
PM MAIL   Вверх
CodeMonkey
Дата 11.7.2008, 12:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1839
Регистрация: 24.6.2008
Где: Россия, Тверь

Репутация: 16
Всего: 89



1. Если используется CreateThread вместо BeginThread, то есть шанс, что IsMultiThread будет равна False. А это в частности ведёт к тому, что менеджер памяти не будет использовать защиту ресурсов. Т.е. одновременный вызов функций перераспределения памяти из разных потоков может вызвать исключение.

2. Как насчёт защиты ресурсов в MessageLogProc?

3. И, кстати, try/finally здесь будет критично, т.к. любое исключение в любом потоке у вас немедленно завершает поток, но не освобождает критическую секцию.

4. Не проверяли - в каком месте именно вываливается AV?

P.S.
Цитата(Riply @  11.7.2008,  05:16 Найти цитируемый пост)
Там все сложнее, чем просто установка IsMultiThread

Да ну? XD  Наверное, мне надо очки помощнее. Я вот смотрю на BeginThread и вижу: подготовка параметров, установка IsMultiThreaded, вызов CreateThread и...  всё  smile





--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
Coder
Дата 11.7.2008, 15:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 733
Регистрация: 13.12.2004

Репутация: 1
Всего: 11



Цитата(CodeMonkey @  11.7.2008,  20:13 Найти цитируемый пост)
1. Если используется CreateThread вместо BeginThread, то есть шанс, что IsMultiThread будет равна False. А это в частности ведёт к тому, что менеджер памяти не будет использовать защиту ресурсов. Т.е. одновременный вызов функций перераспределения памяти из разных потоков может вызвать исключение.

Почему так происходит? По сути IsMultiThread я могу установить сам и спокойно пользоваться CreateThread(). Мне тогда не совсем понятна роль крит секций. Я ведь работаю с указателем на память где хранятся сообщения и в конкретный момент времени с ним должен работать ОДИН поток (перевыделить памяти, добавить сообщение).

Цитата(CodeMonkey @  11.7.2008,  20:13 Найти цитируемый пост)
2. Как насчёт защиты ресурсов в MessageLogProc?

Сделаю. 

Цитата(CodeMonkey @  11.7.2008,  20:13 Найти цитируемый пост)
3. И, кстати, try/finally здесь будет критично, т.к. любое исключение в любом потоке у вас немедленно завершает поток, но не освобождает критическую секцию.

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

Цитата(CodeMonkey @  11.7.2008,  20:13 Найти цитируемый пост)
4. Не проверяли - в каком месте именно вываливается AV?

Отладчик на участок кода не показывает, а в дизассемблере - код не относящийся явно к проекту. Хотя покапать можно.

Цитата(CodeMonkey @  11.7.2008,  20:13 Найти цитируемый пост)
Да ну? XD  Наверное, мне надо очки помощнее. Я вот смотрю на BeginThread и вижу: подготовка параметров, установка IsMultiThreaded, вызов CreateThread и...  всё 

 smile 

PM MAIL   Вверх
Riply
Дата 11.7.2008, 15:32 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Цитата(Coder @  11.7.2008,  09:38 Найти цитируемый пост)
Riply, Все-таки это тестовый пример для тестирования главной процедуры, и не важно в каком состоянии будет поток после выполнения кода, главное чтобы он N число раз выполнил AddLogMessage. 


Поимка AV (точнее: нахождение причины, а не места, где оно происходит) - 
одна из самых сложных операций. Сложнее, пожалуй, только "плавающая" ошибка.
Допустим, мы собираемся тестировать этот код "в хвост и в гриву" smile
У тебя есть уверенность, что N - незакрытых Handle`ов не повлияют на ход тестирования ?
У меня нет. Уж не говоря о том, что их количество ограничено  smile 

И гавное, определись с чем будем работать с BeginThread или CreateThread.

Цитата(CodeMonkey @  11.7.2008,  12:13 Найти цитируемый пост)
Да ну? XD  Наверное, мне надо очки помощнее. Я вот смотрю на BeginThread и вижу: подготовка параметров, установка IsMultiThreaded, вызов CreateThread и...  всё  


"Если на клетке со львом написано "Слон", то не верь глазам своим" (с)
К. Прутков. (Писала по памяти, могла исказить)

Именно из-за кажущейся простоты кода BeginThread, я и пыталась акцентировать на этом внимание,
чтобы не было ползновений использовать CreateThread и вручную ставить IsMultiThreaded.
Там есть "подводные камни".
К сожалению не могу вспомнить, у кого вся эта кухня подробно расписана (сейчас проверила Рихтера - не нашла).
Sorry. Может позже смогу привести ссылку. 

PM MAIL   Вверх
CodeMonkey
Дата 11.7.2008, 16:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1839
Регистрация: 24.6.2008
Где: Россия, Тверь

Репутация: 16
Всего: 89



Цитата(Coder @  11.7.2008,  15:10 Найти цитируемый пост)
По сути IsMultiThread я могу установить сам и спокойно пользоваться CreateThread().

Можете (на мой взгляд, может позже Riply ещё информации добавит).

Цитата(Coder @  11.7.2008,  15:10 Найти цитируемый пост)
Мне тогда не совсем понятна роль крит секций. Я ведь работаю с указателем на память где хранятся сообщения и в конкретный момент времени с ним должен работать ОДИН поток (перевыделить памяти, добавить сообщение).

Совершенно верно. Вы своими критическими секциями защищаете свои глобальные переменные. Внимание, вопрос: кто будет защищать внутренние структуры менеджера памяти? Очевидно, сам менеджер памяти. Так вот, делать он этого не будет, если IsMultiThreaded не установлена.

Цитата(Coder @  11.7.2008,  15:10 Найти цитируемый пост)
Почему так происходит?

Так устроен мир. Делается это для экономии ресурсов в однопоточных приложениях - коих (пока) большинство.

Цитата(Coder @  11.7.2008,  15:10 Найти цитируемый пост)
Если есть такой механизм, это же не значит, что его нужно везде использовать где надо и не надо.

Конкретно в этом случае - надо. Вы понимаете, что используя try/finally вы ГАРАНТИРУЕТЕ освобождение заблокированного ресурса. Как я уже сказал, без использования try/finally при возникновении любого исключения между Enter/LeaveCriticalSection вы навечно блокируете ресурс.

Цитата(Coder @  11.7.2008,  15:10 Найти цитируемый пост)
Ф-ия AddLogMessage может загнуться на выделениях/перевыделениях памяти, но это можно обработать и "вручную".

Как, позвольте, вы собрались обрабатывать вручную AV при перераспределении памяти не используя обработку исключений Delphi?

Цитата(Coder @  11.7.2008,  15:10 Найти цитируемый пост)
Отладчик на участок кода не показывает, а в дизассемблере - код не относящийся явно к проекту. Хотя покапать можно.

Попробуйте проставить по коду AddLogMessage вызовы OutputDebugString. Поймать их можно отладчиком Delphi или DebugView:
http://technet.microsoft.com/en-us/sysinte...s/bb896647.aspx

Например вот как-то так:
Код
procedure AddLogMessage(_LogMessage : PAnsiChar);
var
  _new_size : Cardinal;
begin
  EnterCriticalSection(lCritSect);
  try
    OutputDebugString(PChar(IntToStr(GetCurrentThreadId) + ': entered'));
    try
      if LogMessage=nil then
      begin
        OutputDebugString(PChar(IntToStr(GetCurrentThreadId) + ': LogMessage is nil'));
        LogLen:=StrLen(_LogMessage)+1;
        OutputDebugString(PChar(IntToStr(GetCurrentThreadId) + ': 1.1'));
        LogMessage:=AllocMem(LogLen);
        OutputDebugString(PChar(IntToStr(GetCurrentThreadId) + ': 1.2'));
        StrCopy(LogMessage,_LogMessage);
        OutputDebugString(PChar(IntToStr(GetCurrentThreadId) + ': 1.3'));
      end
      else
      begin
        OutputDebugString(PChar(IntToStr(GetCurrentThreadId) + ': LogMessage is not nil'));
        _new_size:=LogLen+StrLen(_LogMessage)+2;
        OutputDebugString(PChar(IntToStr(GetCurrentThreadId) + ': 2.1'));
        ReallocMem(LogMessage, _new_size);
        OutputDebugString(PChar(IntToStr(GetCurrentThreadId) + ': 2.2'));
        LogLen:=_new_size;
        StrCat(LogMessage,#13#10);
        OutputDebugString(PChar(IntToStr(GetCurrentThreadId) + ': 2.3'));
        StrCat(LogMessage,_LogMessage);
        OutputDebugString(PChar(IntToStr(GetCurrentThreadId) + ': 2.4'));
      end;
    except
      on E: Exception do
        OutputDebugString(PChar(IntToStr(GetCurrentThreadId) + ': exception "' + E.Message + '"'));
    end;
    OutputDebugString(PChar(IntToStr(GetCurrentThreadId) + ': leaved'));
  finally
    LeaveCriticalSection(lCritSect);
  end;
end;


P.S.
Цитата(Riply @  11.7.2008,  15:32 Найти цитируемый пост)
К сожалению не могу вспомнить, у кого вся эта кухня подробно расписана (сейчас проверила Рихтера - не нашла).

Ну Рихтер говорил о аналогичных функциях в MS C++, а не в Delphi. Если же смотреть на исходный код BeginThread - то видно, что ничего дополнительного кроме установки IsMultiThreaded она не делает. Если были какие-то проблемы, которые решились сменой CreateThread на BeginThread, то они могут быть связаны (помимо IsMultiThreaded) с прототипом функции - он разный у BeginThread и CreateThread.


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
Riply
Дата 11.7.2008, 18:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Комодератор
Сообщений: 572
Регистрация: 27.3.2007
Где: St. Petersburg

Репутация: 21
Всего: 32



Ссылку, пока, не нашла, но рано или поздно найду. Чес. слово smile
Зато расскажу одну историю. 
Она не имеет отношения к теме, но показывает что не всегда можно безболезненно убирать "ненужные" вызовы.
Имеем такую ф-ию:
Код

procedure InitializeObjectAttributesEx(const pObjAttr: POBJECT_ATTRIBUTES; const pObjName: PUNICODE_STRING; const Attributers: ULONG; const hRootObj: THandle; const pSecAttr: PSECURITY_ATTRIBUTES);
begin
 if pSecAttr <> nil then
  with pSecAttr^ do
   if bInheritHandle
    then InitializeObjectAttributes(pObjAttr, pObjName, Attributers or OBJ_INHERIT, hRootObj, lpSecurityDescriptor)
    else InitializeObjectAttributes(pObjAttr, pObjName, Attributers, hRootObj, lpSecurityDescriptor)
 else InitializeObjectAttributes(pObjAttr, pObjName, Attributers, hRootObj, nil);
end;

Из нее видно, что ее вызов с параметром pSecAttr = nil сводится к вызову InitializeObjectAttributes.
Т.е., казалось бы, в этом случае, их можно менять друг на друга. Ан нет.
функция:
Код

function Nt_OpenSection(const pName: PUNICODE_STRING; const DesAccess: LongWord; InheritHandle: Boolean; var hMapObj: THandle): NTSTATUS;
var
 ObjAttr: OBJECT_ATTRIBUTES;
begin
 InitializeObjectAttributesEx(@ObjAttr, pName, OBJ_CASE_INSENSITIVE, 0, nil);
 if DesAccess = FILE_MAP_COPY
  then Result := NtOpenSection(@hMapObj, FILE_MAP_READ, @ObjAttr)
  else Result := NtOpenSection(@hMapObj, DesAccess, @ObjAttr);
end;
Не всегда корректно отрабатывала (зависило от расположения звезд). Это редко, но бывало.
Но если в ней InitializeObjectAttributesEx заменить на InitializeObjectAttributes - эффет исчезал.
Т.к. он (эффект) очень редко появлялся, я положила много времени на поимку бага.
А он вот в чем: менялось "выравнивание" ObjAttr 32-х битную границу.
Как результат, при некоторых "билдах" NtOpenSection начинала нецензурно ругаться.
В итоге, для "надежного" использования InitializeObjectAttributesEx пришлось создавать
такую структуру:

Код

type
  OBJECT_ATTRIBUTES_ALIGN = packed record
   _Stuff: array[0..Pred(SizeOf(OBJECT_ATTRIBUTES) + SizeOf(Int64))] of Byte;
   function pObjAttr: POBJECT_ATTRIBUTES;
  end;

function OBJECT_ATTRIBUTES_ALIGN.pObjAttr: POBJECT_ATTRIBUTES;
begin
 Result := POBJECT_ATTRIBUTES(ROUND_UP64(LongWord(@Self)));
end;

P.S.
 Если кто захочет воспроизвести эффект, ему придется набраться терпения,
 ибо он может зависить от взаиморасположения ф-ий и структур, номера билда и фазы луны smile
P.P.S.
 Все это происходило под BDS 2006. Приведенный код выдергивался и чуть "правился на лету" - возможны ляпы smile
Самый последий P.P.P.S smile
Углубляться в обсуждение выравниваний не очень хочется, тем более ветка совсем не про это smile
PM MAIL   Вверх
MetalFan
Дата 11.7.2008, 21:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 16
Всего: 128



Цитата(CodeMonkey @  11.7.2008,  12:13 Найти цитируемый пост)
Да ну? XD  Наверное, мне надо очки помощнее. Я вот смотрю на BeginThread и вижу: подготовка параметров, установка IsMultiThreaded, вызов CreateThread и...  всё 

очки по-мощнее пожалуй пригодятся...
ибо:
Код

{$IFDEF MSWINDOWS}
function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord;
  ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord;
  var ThreadId: LongWord): Integer;
var
  P: PThreadRec;
begin
  if Assigned(SystemThreadFuncProc) then
    P := PThreadRec(SystemThreadFuncProc(ThreadFunc, Parameter))
  else
  begin
    New(P);
    P.Func := ThreadFunc;
    P.Parameter := Parameter;
  end;
  IsMultiThread := TRUE;
  Result := CreateThread(SecurityAttributes, StackSize, @ThreadWrapper, P,
    CreationFlags, ThreadID);
end;

CreateThread вызывается не с переданной ThreadFunc, а с борландовской оберткой ThreadWrapper ;)
а в ней, на сколько я понимаю, еще корректно инициализируется обработка исключений.

Это сообщение отредактировал(а) MetalFan - 11.7.2008, 21:08


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
CodeMonkey
Дата 11.7.2008, 22:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1839
Регистрация: 24.6.2008
Где: Россия, Тверь

Репутация: 16
Всего: 89



P.S.
Цитата(MetalFan @  11.7.2008,  21:08 Найти цитируемый пост)
CreateThread вызывается не с переданной ThreadFunc, а с борландовской оберткой ThreadWrapper 

Да, вы правы, наверное, зря я об этом не упомянул. Просто речь шла в контексте:
Цитата(Riply @  11.7.2008,  05:16 Найти цитируемый пост)
Там все сложнее, чем просто установка IsMultiThread

Цитата(Riply @  11.7.2008,  15:32 Найти цитируемый пост)
...чтобы не было ползновений использовать CreateThread и вручную ставить IsMultiThreaded. Там есть "подводные камни".

Т.е. касательно побочных эффектов в виде IsMultiThreaded. Поэтому я упомянул только действия до CreateThread. 


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: WinAPI и системное программирование"
Snowybartram
MetalFanbems
PoseidonRrader
Riply

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Delphi обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи
  • 99% ответов по WinAPI можно найти в MSDN Library, оставшиеся 1% здесь

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, bartram, MetalFan, bems, Poseidon, Rrader, Riply.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: WinAPI и системное программирование | Следующая тема »


 




[ Время генерации скрипта: 0.0669 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.