Fix: 상태머신 루프 블로킹 문제 수정 - SPS 이벤트 핸들러 비동기 처리 및 타임아웃 보호 추가
- sm_SPS 이벤트 핸들러에서 장치 연결 및 상태 전송을 비동기로 처리 - DeviceConnectionWorker 스레드로 장치 연결 분리 - SPS(1초), Running(2초) 타임아웃 보호 추가 - 상태머신 모니터링 디버그 창 추가 (fStateMachineDebug) - F11/F12 단축키로 스레드 덤프 및 디버그 브레이크 지원 - RaiseMessage 이벤트 비동기 처리로 로그 블로킹 방지
This commit is contained in:
@@ -87,18 +87,64 @@ namespace Project.StateMachine
|
||||
|
||||
private Boolean _isthreadrun;
|
||||
public Boolean IsThreadRun { get { return _isthreadrun; } }
|
||||
public int LoopCount { get { return _loopCount; } }
|
||||
public DateTime LastLoopTime { get { return _lastLoopTime; } }
|
||||
|
||||
private int _loopCount = 0;
|
||||
private DateTime _lastLoopTime = DateTime.Now;
|
||||
|
||||
void Loop()
|
||||
{
|
||||
_isthreadrun = true;
|
||||
if (GetMsgOpt(EMsgOpt.NORMAL)) RaiseMessage("SM", "Start");
|
||||
while (bLoop)
|
||||
|
||||
try
|
||||
{
|
||||
RunSpeed = DateTime.Now - UpdateTime; //이전업데이트에서 현재 시간의 오차 한바퀴 시간이 표시됨
|
||||
UpdateTime = DateTime.Now;
|
||||
while (bLoop)
|
||||
{
|
||||
_loopCount++;
|
||||
_lastLoopTime = DateTime.Now;
|
||||
|
||||
// 루프 동작 확인용 로그 (10초마다)
|
||||
if (_loopCount % 10000 == 0)
|
||||
{
|
||||
RaiseMessage("SM-LOOP", $"Loop alive - Count:{_loopCount}, bLoop:{bLoop}, Step:{Step}");
|
||||
}
|
||||
|
||||
RunSpeed = DateTime.Now - UpdateTime; //이전업데이트에서 현재 시간의 오차 한바퀴 시간이 표시됨
|
||||
UpdateTime = DateTime.Now;
|
||||
|
||||
//항상 작동하는 경우
|
||||
SPS?.Invoke(this, new EventArgs());
|
||||
try
|
||||
{
|
||||
var spsHandler = SPS;
|
||||
if (spsHandler != null)
|
||||
{
|
||||
var spsStartTime = DateTime.Now;
|
||||
var spsTask = System.Threading.Tasks.Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
spsHandler(this, new EventArgs());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RaiseMessage("SM-ERROR-SPS", $"SPS Exception: {ex.Message}\n{ex.StackTrace}");
|
||||
}
|
||||
});
|
||||
|
||||
// 타임아웃 1초
|
||||
if (!spsTask.Wait(1000))
|
||||
{
|
||||
var elapsed = (DateTime.Now - spsStartTime).TotalSeconds;
|
||||
RaiseMessage("SM-TIMEOUT", $"SPS 이벤트 타임아웃 ({elapsed:F2}초 초과) - Step:{Step}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RaiseMessage("SM-ERROR-SPS", $"SPS Exception: {ex.Message}\n{ex.StackTrace}");
|
||||
}
|
||||
|
||||
//작동스텝이 변경되었다면 그것을 알림 처리한다.
|
||||
if (GetNewStep != Step)
|
||||
@@ -118,22 +164,47 @@ namespace Project.StateMachine
|
||||
|
||||
|
||||
//동작중에 발생하는 이벤트
|
||||
if (Running != null)
|
||||
var runningHandler = Running;
|
||||
if (runningHandler != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Running(this, new RunningEventArgs(Step, firstRun, StepRunTime));
|
||||
var runningStartTime = DateTime.Now;
|
||||
var runningTask = System.Threading.Tasks.Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
runningHandler(this, new RunningEventArgs(Step, firstRun, StepRunTime));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RaiseMessage("SM-ERROR-RUNNING", $"Running Exception: {ex.Message}\n{ex.StackTrace}");
|
||||
}
|
||||
});
|
||||
|
||||
// 타임아웃 2초
|
||||
if (!runningTask.Wait(2000))
|
||||
{
|
||||
var elapsed = (DateTime.Now - runningStartTime).TotalSeconds;
|
||||
RaiseMessage("SM-TIMEOUT", $"Running 이벤트 타임아웃 ({elapsed:F2}초 초과) - Step:{Step}, FirstRun:{firstRun}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RaiseMessage("SM-ERROR", ex.Message);
|
||||
RaiseMessage("SM-ERROR-RUNNING", $"Running Exception: {ex.Message}\n{ex.StackTrace}");
|
||||
}
|
||||
} System.Threading.Thread.Sleep(1);
|
||||
}
|
||||
|
||||
System.Threading.Thread.Sleep(1);
|
||||
}
|
||||
_isthreadrun = false;
|
||||
if (GetMsgOpt(EMsgOpt.NORMAL)) RaiseMessage("SM", "Stop");
|
||||
catch (Exception ex)
|
||||
{
|
||||
RaiseMessage("SM-FATAL", $"Loop Fatal Exception: {ex.Message}\n{ex.StackTrace}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isthreadrun = false;
|
||||
if (GetMsgOpt(EMsgOpt.NORMAL)) RaiseMessage("SM", $"Stop - LoopCount:{_loopCount}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +246,18 @@ namespace Project.StateMachine
|
||||
OldStep = _step;
|
||||
_step = newstep_;
|
||||
_newstep = newstep_;
|
||||
StepChanged?.Invoke(this, new StepChangeEventArgs(OldStep, newstep_));
|
||||
|
||||
// 비동기로 이벤트 발생 (블로킹 방지)
|
||||
var handler = StepChanged;
|
||||
if (handler != null)
|
||||
{
|
||||
var args = new StepChangeEventArgs(OldStep, newstep_);
|
||||
System.Threading.ThreadPool.QueueUserWorkItem(_ =>
|
||||
{
|
||||
try { handler(this, args); }
|
||||
catch { /* 이벤트 핸들러 예외 무시 */ }
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -284,7 +366,18 @@ namespace Project.StateMachine
|
||||
{
|
||||
var ostep = _step;
|
||||
OldStep = _step; _step = _newstep;
|
||||
StepChanged?.Invoke(this, new StepChangeEventArgs(ostep, _step));
|
||||
|
||||
// 비동기로 이벤트 발생 (블로킹 방지)
|
||||
var handler = StepChanged;
|
||||
if (handler != null)
|
||||
{
|
||||
var args = new StepChangeEventArgs(ostep, _step);
|
||||
System.Threading.ThreadPool.QueueUserWorkItem(_ =>
|
||||
{
|
||||
try { handler(this, args); }
|
||||
catch { /* 이벤트 핸들러 예외 무시 */ }
|
||||
});
|
||||
}
|
||||
} //171214
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user