성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>Thread.Abort로 인해 프로세스가 종료되는 경우</h1> <p> "제프리 리처의 CLR via C#" 책을 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 제프리 리처의 CLR via C# ; <a target='tab' href='http://www.yes24.com/24/goods/15169403'>http://www.yes24.com/24/goods/15169403</a> </pre> <br /> 675페이지에 다음과 같은 내용이 있습니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> CLR은 스레드가 ThreadAbortException을 발생시키도록 한다. ... 다른 예외와는 다르게 ThreadAbortException은 처리되지 않아도 응용 프로그램을 종료시키지 않는다. CLR은 이 예외를 조용히 먹어버리고 해당 스레드를 종료시킨다.<br /> </div><br /> <br /> 이는 다음과 같은 코드로 재현해 볼 수 있는데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.IO; using System.Threading; class Program { static void Main(string[] args) { ThrowException(); } private static void ThrowException() { EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.ManualReset); EventWaitHandle waitForever = new EventWaitHandle(false, EventResetMode.ManualReset); Thread t1 = new Thread(() => { ewh.Set(); try { // throw new ApplicationException("Exception occurred!!!"); waitForever.WaitOne(); } catch (Exception e) { Console.WriteLine(e.Message); } }); t1.Start(); ewh.WaitOne(); <span style='color: blue; font-weight: bold'>t1.Abort();</span> t1.Join(); } } </pre> <br /> 외부 스레드에서 t1.Abort를 호출해 ThreadAbortException을 발생시켰고, 내부의 try/catch 메시지가 실행되는 걸로 확인할 수 있지만 분명히 프로그램은 종료되지 않습니다. (<a target='tab' href='http://www.sysnet.pe.kr/2/0/10863'>legacyUnhandledExceptionPolicy</a> 속성 값이 false여도 종료가 안됩니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그런데, 예외적인 상황이 하나 있습니다. 바로 ASP.NET의 "요청을 처리하는 스레드"를 외부에서 강제로 Thread.Abort 시킨 경우입니다.<br /> <br /> 재현은 ASP.NET 웹 폼 프로젝트를 만들고 다음과 같이 코딩을 하면 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Threading; namespace WebApplication1 { public partial class Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { <span style='color: blue; font-weight: bold'>Thread currentThread = Thread.CurrentThread;</span> EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.ManualReset); Thread t1 = new Thread((objThread) => { Thread requestThread = objThread as Thread; <span style='color: blue; font-weight: bold'>requestThread.Abort(); // Button1_Click 메서드를 실행하는 스레드를 종료, // 원치 않게 프로세스도 함께 종료됨.</span> ewh.Set(); }); t1.Start(currentThread); ewh.WaitOne(); } } } </pre> <br /> 여기서 궁금한 점이 하나 생깁니다. 그렇다면 동일하게 ThreadAbortException을 발생시키는 Response.End를 호출하는 경우에는 왜 프로세스가 종료되지 않느냐는 점입니다. 이것에는 ASP.NET 내부의 배려가 있음을 Response.End 메서드를 .NET Reflector를 이용해 소스 코드를 보면 알아낼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // HttpResponse 타입의 End 메서드 public void End() { if (this._context.IsInCancellablePeriod) { <span style='color: blue; font-weight: bold'>AbortCurrentThread();</span> } else { this._endRequiresObservation = true; if (!this._flushing) { this.Flush(); this._ended = true; if (this._context.ApplicationInstance != null) { this._context.ApplicationInstance.CompleteRequest(); } } } } [SecurityPermission(SecurityAction.Assert, ControlThread=true)] private static void AbortCurrentThread() { <span style='color: blue; font-weight: bold'>Thread.CurrentThread.Abort(new HttpApplication.CancelModuleException(false));</span> } </pre> <br /> 보는 바와 같이, HttpApplication.CancelModuleException 타입의 인스턴스를 넘기는 차이가 있는데 정말 이것으로 인해 비정상 종료가 되지 않는지 다음과 같은 코드를 통해 확인해 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Reflection; using System.Runtime.Remoting; using System.Threading; using System.Web; namespace WebApplication1 { public partial class Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { Thread currentThread = Thread.CurrentThread; EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.ManualReset); Thread t1 = new Thread((objThread) => { Thread requestThread = objThread as Thread; Assembly asm = Assembly.GetAssembly(typeof(HttpApplication)); ObjectHandle instance = <span style='color: blue; font-weight: bold'>AppDomain.CurrentDomain.CreateInstance(asm.FullName, "System.Web.HttpApplication+CancelModuleException"</span>, false, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { false }, null, null); object objValue = instance.Unwrap(); <span style='color: blue; font-weight: bold'>requestThread.Abort(objValue);</span> ewh.Set(); }); try { t1.Start(currentThread); while (true) { System.Diagnostics.Trace.WriteLine("ThreadID: " + AppDomain.GetCurrentThreadId()); Thread.Sleep(500); } } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.ToString()); } } } } </pre> <br /> 실행해 보면, 이번에는 해당 요청을 실행하는 스레드는 ThreadAbortException과 함께 처리를 중지하지만 w3wp.exe (또는 iisexpress.exe)는 종료하지 않고 실행이 계속됩니다. <br /> <br /> 역시, 내부를 살펴보면 System.Web.HttpApplication 타입의 ExecuteStep 메서드에서 CancelModuleException 인스턴스가 설정된 ThreadAbortException 예외에 대해서는 Thread.ResetAbort를 호출함으로써 예외 전파를 중지하고 있음을 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > internal Exception ExecuteStep(IExecutionStep step, ref bool completedSynchronously) { Exception exception = null; try { try { if (step.IsCancellable) { this._context.BeginCancellablePeriod(); try { step.Execute(); } finally { this._context.EndCancellablePeriod(); } this._context.WaitForExceptionIfCancelled(); } else { step.Execute(); } if (!step.CompletedSynchronously) { completedSynchronously = false; return null; } } // ...[생략]... } catch (ThreadAbortException exception4) { if ((exception4.ExceptionState != null) && <span style='color: blue; font-weight: bold'>(exception4.ExceptionState is CancelModuleException)</span>) { if (((CancelModuleException) exception4.ExceptionState).Timeout) { exception = new HttpException(SR.GetString("Request_timed_out"), null, 0xbb9); PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_TIMED_OUT); } else { exception = null; this._stepManager.CompleteRequest(); } <span style='color: blue; font-weight: bold'>Thread.ResetAbort();</span> } } completedSynchronously = true; return exception; } </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
3978
(왼쪽의 숫자를 입력해야 합니다.)