안녕하세요. 어제 System 명령 사용에 대한 질문을 했는데
제가 사용했던 소스를 다시 올리겠습니다.
현재 제가 개발한 프로그램이 20대 정도에서 운용하고 있는데,
평균 일주일에 2, 3번 정도 프로그램이 다운되네요.
장비 사양 및, 장비 부하등은 똑 같다고 보시면 됩니다.
일단 제가 개발한 환경은
- Windows 2000 Pro
- Delphi 5
그리고 프로그램 주요 특징 으로는
- 120 개의 스래드가 사용이 됨
- 스래드 멤버 변수도 있지만 전역 변수에 참조 되는 일도 있음.
- 전역 변수는 각각의 스래드가 사용될 수 있도록 120개의 개체가 저장된 TStringList 로 구성됨.
//----------------------------------------------------------------------------------------------------------------------//
// 첫번째의 경우
//----------------------------------------------------------------------------------------------------------------------//
* Doctor Watson Log
스레드 Id 0x708의 상태 덤프
eax=20162112 ebx=16262190 ecx=00000035 edx=00000008 esi=00000020 edi=16262190
eip=00401fdb esp=0642e97c ebp=005bd488 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
함수: <nosymbols>
00401fbb c1fa02 sar edx,0x2
00401fbe a190d45b00 mov eax,[005bd490] ds:005bd490=00136dc0
00401fc3 8b4490f4 mov eax,[eax+edx*4+0xf4] ds:0086d5db=????????
00401fc7 85c0 test eax,eax
00401fc9 7510 jnz 0040c0db
00401fcb a190d45b00 mov eax,[005bd490] ds:005bd490=00136dc0
00401fd0 895c90f4 mov [eax+edx*4+0xf4],ebx ds:0086d5db=????????
00401fd4 895b04 mov [ebx+0x4],ebx ds:16acf762=????????
00401fd7 891b mov [ebx],ebx ds:16262190=7257202e
00401fd9 eb3a jmp 0040ab15
오류 -> 00401fdb 8b10 mov edx,[eax] ds:20162112=????????
00401fdd 894304 mov [ebx+0x4],eax ds:16acf762=????????
00401fe0 8913 mov [ebx],edx ds:16262190=7257202e
00401fe2 8918 mov [eax],ebx ds:20162112=????????
00401fe4 895a04 mov [edx+0x4],ebx ds:0086d5da=????????
00401fe7 eb2c jmp 0040a115
00401fe9 81fe003c0000 cmp esi,0x3c00
00401fef 7c0d jl 0040aafe
00401ff1 8bd6 mov edx,esi
00401ff3 8bc7 mov eax,edi
00401ff5 e802ffffff call 00401efc
00401ffa 84c0 test al,al
*----> 스택 역 추적 <----*
FramePtr ReturnAd Param#1 Param#2 Param#3 Param#4 Function Name
005BD488 00000000 00136DC0 00137FB4 00138164 00000000 !<nosymbols>
//----------------------------------------------------------------------------------------------------------------------//
// 소스 및 소스 설명
//----------------------------------------------------------------------------------------------------------------------//
* 120개의 스래드가 운용중에 있습니다. 여기서 trdChannel 은 스래드입니다.
* 각각의 스래드에서 사용하는 변수는 채널 변수와 전역 변수로 구성되여 있습니다.
* 채널 변수는
* 전역 변수는
* 전역 변수 선언은 gPortInfoList : TStringList 로 처리 했습니다.
* 전역 변수 초기 데이터 입력은 프로그램 시작시 다음과 같이 입력을 합니다.
recPortInfo : TPortInfo;
recPortInfo := TPortInfo.Create;
recPortInfo..... <- 데이터 초기값 입력
recPortInfo..... <- 데이터 초기값 입력
recPortInfo..... <- 데이터 초기값 입력
gPortInfoList.AddObject(Trim(recPortInfo.PortExt), recPortInfo);
* TPortInfo 구조는 다음과 같은 타입으로 구성되여 있습니다.
PortNo : SmallInt
PortExt : ShortString
PortAgentID : ShortString
PortPos : ShortString
PortACD : ShortString
PortDB : ShortString
StartFile : ShortString
EndFile : ShortString
PortCount : Integer
StartSno : Integer
EndSno : Integer
ScenarioPath : ShortString
ScenarioDesc : ShortString
PortMode : String
TruncInfo : String
AniInfo : String
AniInfoDefault : String
DnisData : String
R2Tone : String
DnisDataDefault : String
ExtData : String
ExtDataDefault : String
CallData : String
CallDataDefault : String;
AcdStatus : Integer;
AcdStatusDefault : Integer;
AcdAgent : Integer;
AcdAgentDefault : Integer;
HeldCount : Integer;
HeldTime : Integer;
HeldServer : Integer;
TransferFlag : Integer;
SVFlag : Integer;
strSVReturnCode : String
strSVScore : String;
Call_ID01 : String
Call_ID02 : String
Call_ID03 : string;
ExtraData : string;
ExtraODNData : string;
PartyDeleted : String
PartyChanged : String;
ServiceStart : String
ServiceStop : String;
PortLock : Boolean;
PortState : Byte;
iSleepCnt : Integer;
bCTIDisconnect : Boolean;
PortActionFlg : Boolean;
PortApplyCheck : Boolean;
PortMentApplyCheck : Boolean;
PortMentActionFlg : Boolean;
PortStopCheck : Integer;
bMohHangup : Boolean;
vMohThread : TMOHThread;
PortApplyCompleteFlag : Boolean;
CTIDisconnectEvent : Integer;
iCSNo : Integer
iCSEvent : Integer;
strCSFile : String
strCSHour : String;
CallStartTime : String;
strPlayFile : String;
strDigitCount : String;
strTimeout : String;
strBaseScore : String;
strThisDN : String
strOtherDN : String
strANI : String;
bEventPartyDeleted : Boolean;
bMOHThreadDelay : Boolean;
* 이 부분에서 처리할때 InsertFree 에러가 나오는 것 같습니다.
// 스래드가 시작할 때 맨처음에 수행되는 모듈입니다.
procedure trdChannel.Service_Start(bFlag : Boolean);
var curDate: TDateTime;
strLog, strDesc, strResult : String;
iRc : Integer;
begin
curDate := Now;
EnterCriticalSection(gPortCritical);
with TPortInfo(gPortInfoList.Objects[t_Index]) do
begin
Inc(PortCount);
ServiceStart := FormatDateTime('dd hh:nn:ss', curDate);
ServiceStop := '';
// CallTrace 시간 형식 변경.
CallStartTime := FormatDateTime('yyyymmddhhnnss',curDate);
// Channel Status display
Set_StateCount(t_Index, PortState, STATUS_ANSWER);
PortState := STATUS_ANSWER;
end;
LeaveCriticalSection(gPortCritical);
end;
// 화면상에 정보를 보여주기 위하여 Count 정보를 증가 및 변동하는 모듈입니다.
procedure Set_StateCount(iIndex, iOldState, iNewState : Integer);
var vInx : Integer;
curTime : String;
begin
EnterCriticalSection(gGlobalCountCritical);
TPortInfo(gPortInfoList.Objects[iIndex]).PortState := iNewState;
Dec(gStatusCount[iOldState]);
Inc(gStatusCount[iNewState]);
case iNewState of
STATUS_ANSWER : begin
// Peak Time Count
curTime := FormatDateTime('hhnn', Now);
// 시간대별 Call Count 누적
vInx := StrToInt(FormatDateTime('hh', Now));
Inc(gTimeCount[vInx]);
// Acd Group별 Call Count 누적
vInx := gAcdGroup.IndexOf(TPortInfo(gPortInfoList.Objects[iIndex]).PortACD);
if vInx > -1 then
Inc(TAcdGroupClass(gAcdGroup.Objects[vInx]).CallCount);
end;
end;
LeaveCriticalSection(gGlobalCountCritical);
end;
//----------------------------------------------------------------------------------------------------------------------//
// 두번째의 경우
//----------------------------------------------------------------------------------------------------------------------//
* 두번째의 경우는 닥터왓슨 로그는 나오지 않았지만 InsertError에 대하여 Try Except 가 발생한 부분입니다.
아래의 로직을 수행하면서 E.Message 내용이 나왔습니다. 해당 Address가 InsertFree 입니다.
(Access violation at address 00401FDB in module 'MT.exe'. Read of address 20162112)
그래서 Try Except 안에 있는 부분에서 잘못된 것이라고 생각합니다.
// 사용자 정의 변수의 값을 지정한 값으로 초기화 한다.
// Data1 : 사용자 정의 변수
// Data2 : 초기값
function FCN_Initialize(iIndex, iPort, iSnoNo, iSnoType: Integer; sSnoPath, sSnoFile: String): Integer;
var
recFuncTable: TFuncTableRecord;
iPos1 : integer;
strLog, strResult, sResult, strOne1, strData1 : string;
lOk : Boolean;
begin
// FuncTable Table에서 값을 가져온다.
if not Read_FuncTable(iIndex, iPort, iSnoNo, sSnoFile, recFuncTable) then
begin
Result := EVT_SQL_ERROR;
WriteTraceLog(ALL_LOG, iPort, iSnoNo, iSnoType, Result, 'FCN_Initialize: EVT_SQL_ERROR');
exit;
end;
strLog := '';
lOk := True;
try
strData1 := recFuncTable.strData1;
if (not IsNull(strData1)) then
if (strData1[Length(strData1)] <> UDV_END_DELI_COMMA) then strData1 := strData1 + UDV_END_DELI_COMMA;
while strData1 <> '' do
begin
iPos1 := Pos(UDV_END_DELI_COMMA, strData1);
if iPos1 < 1 then Break;
strOne1 := Copy(strData1, 1, iPos1-1); //UDV Value
if strOne1 = '' then Break;
// 전역 변수 저장함. gPortInfoList 의 iIndex에 해당하는 개체에 데이터 저장
Result := Set_UdvValue(iIndex, Trim(strOne1), '',
'FCN_Initialize', EVT_NEXT_SCENARIO, EVT_REALCT_HANGUP);
if Result <> EVT_NEXT_SCENARIO then
begin
lOk := False;
break;
end;
Delete(strData1, 1, iPos1);
Sleep(1);
end;
except
on E: Exception do
begin
strResult := E.Message;
Result := EVT_REALCT_HANGUP;
strLog := 'FCN_Initialize: Except Error *##*-' + strResult;
WriteTraceLog(ALL_LOG, iPort, 0, 0, 0, strLog);
exit;
end;
end;
end;
// gPortList 는 시스템 시작시 각각의 스래드 ID 값을 저장시킵니다.
vPort := trdChannel.Create();
gPortList.AddObject(FormatFloat('0000', iPortIndex{n-1}), vPort);
// gUserVarList 는 시스템 시작시 각각의 스래드가 가지고 있어야할 기본 정보를 저장하는 리스트입니다.
recUdvClass := TUDVRecord.Create;
with recUdvClass do
begin
// 자신의 채널에서 쓸 UDV를 저장한다.
Name := FieldByName('Name').AsString;
DataType := FieldByName('DataType').AsString;
InitData := FieldByName('InitData').AsString;
Des := FieldByName('Des').AsString;
gUserVarList.AddObject(UpperCase(Name), recUdvClass);
end;
// m_UdvList 는 각각의 스래드가 가지고 있는 멤버 변수입니다. 이 값은 gUserVarList 값을 처음에 초기화 합니다.
// 그래서 스래드가 처음에 시작할 때 초기 값을 gUserVarList 을 이용하면서 로직을 수행하다가
// 다기 처음에 수행할 때 값을 다시 초기화 합니다.
// 스래드가 수행중에 스래드가 가지고 있는 멤버 변수 값을 Write 하는 모듈입니다.
function Set_UDVValue(const iIndex : Integer; const vUdv : String; const vData : String;
const sFuncName : String; const iSuccess : Integer; const iFail : Integer) : Integer;
var vInx : Integer;
recUdv : TUDVRecord;
begin
Result := iSuccess;
try
vInx := Get_UDVIndex(iIndex, Trim(vUDV));
if vInx > -1 then
begin
recUdv := TUdvRecord(trdChannel(gPortList.Objects[iIndex]).m_UdvList.Objects[vInx])
recUdv.InitData := vData;
end else
begin
Result := iFail;
end;
except
Result := iFail;
end;
end;
function Get_UDVIndex(const iIndex : Integer; const vUDV : String) : Integer;
begin
Result := -1;
if( vUDV <> '') then
begin
if vUDV[1] = UDV_START_DELI_AT then
Result := gUserVarList.IndexOf(Copy(vUDV, 2, Length(vUDV)));
end;
end;