성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
글쓰기
제목
이름
암호
전자우편
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'>AsyncTaskMethodBuilder.Create() 메서드 동작 방식</h1> <p> 일반 메서드에 async 예약어가 붙게 되면 다음과 같은 상태 머신 객체로 메서드의 구현이 바뀝니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [AsyncStateMachine(typeof(TaskDelay_StateMachine)), DebuggerStepThrough] private Task TaskDelay() { TaskDelay_StateMachine stateMachine = new TaskDelay_StateMachine { _this = this, _builder = <span style='color: blue; font-weight: bold'>AsyncTaskMethodBuilder.Create</span>(), _state = -1 }; stateMachine._builder.<span style='color: blue; font-weight: bold'>Start</span><TaskDelay_StateMachine>(ref stateMachine); return stateMachine._builder.Task; } </pre> <br /> 그중에서 AsyncTaskMethodBuilder.Create 메서드가 어떤 일을 하는지 궁금했습니다. Reflector 도구로 보면 다음과 같이 단순히 기본 생성자를 호출하는 것에 불과합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [__DynamicallyInvokable] public static AsyncTaskMethodBuilder Create() { return <span style='color: blue; font-weight: bold'>new AsyncTaskMethodBuilder</span>(); } </pre> <br /> 일단, public static 메서드이기 때문에 외부에서도 마음 편하게 직접 실행해 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > AsyncTaskMethodBuilder asyncBuilder = AsyncTaskMethodBuilder.Create(); </pre> <br /> 그리고 asyncBuilder 인스턴스를 Visual Studio의 디버거 watch 창으로 보면 다음과 같은 출력을 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > asyncBuilder + <span style='color: blue; font-weight: bold'>Task Id = 1, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"</span> + ObjectIdForDebugger Id = 1, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}" - <span style='color: blue; font-weight: bold'>m_builder {System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Threading.Tasks.VoidTaskResult>}</span> + Task Id = 1, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}" + <span style='color: blue; font-weight: bold'>m_coreState {System.Runtime.CompilerServices.AsyncMethodBuilderCore}</span> </pre> <br /> 위의 출력에서 기본 생성자만 호출했음에도 불구하고 AsyncTaskMethodBuilder 객체의 내부 변수 m_builder는 값이 채워져 있다는 점입니다. 이게 어떻게 가능할까요? ^^<br /> <br /> 왜냐하면 m_builder 필드의 타입인 AsyncTaskMethodBuilder<System.Threading.Tasks.VoidTaskResult>가 struct 타입이기 때문입니다. 마찬가지로 m_coreState의 객체도 null이 아닐 수 있는 것이 AsyncMethodBuilderCore 타입이 struct이기 때문입니다.<br /> <br /> 따라서, AsyncTaskMethodBuilder.Create() 메서드 호출 하나로 생성되는 객체는 AsyncTaskMethodBuilder, AsyncTaskMethodBuilder<VoidTaskResult>, AsyncMethodBuilderCore가 됩니다.<br /> <br /> 그런데, 여기서 asyncBuilder의 Task 속성도 값을 보여주고 있습니다. <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Id = 1, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}" </pre> <br /> 왜냐하면, Task 속성은 다음과 같이 값이 없으면 기본 Task 객체를 생성해서 반환하기 때문입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [__DynamicallyInvokable] public Task<TResult> Task { [__DynamicallyInvokable] get { Task<TResult> task = this.m_task; <span style='color: blue; font-weight: bold'>if (task == null) { this.m_task = task = new Task<TResult>(); }</span> return task; } } </pre> <br /> 그런데, 이렇게 생성된 m_task의 디버거 출력값이 왜 Id = 1, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"로 되는 걸까요?<br /> <br /> m_task의 타입인 Task<TResult>는 ToString에 대한 재정의도 없습니다. 대신 디버거가 값을 출력할 수 있도록 다음과 같은 특성을 달고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [...[생략]..., DebuggerDisplay(<span style='color: blue; font-weight: bold'>"Id = {Id}, Status = {Status}, Method = {DebuggerDisplayMethodDescription}, Result = {DebuggerDisplayResultDescription}"</span>), ...[생략]...)] public class Task<TResult> : Task { // ...[생략]... } </pre> <br /> 즉, Id, Status, Method, DebuggerDisplayMethodDescription, DebuggerDisplayResultDescription 값을 보여주게 됩니다.<br /> <br /> Id 속성도 접근할 때 없으면 생성하는 방식이라,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [__DynamicallyInvokable] public int get_Id() { <span style='color: blue; font-weight: bold'>if (this.m_taskId == 0) { int num = NewId(); Interlocked.CompareExchange(ref this.m_taskId, num, 0); }</span> return this.m_taskId; } internal static int NewId() { int taskID = 0; do { <span style='color: blue; font-weight: bold'>taskID = Interlocked.Increment(ref s_taskIdCounter);</span> } while (taskID == 0); TplEtwProvider.Log.NewID(taskID); return taskID; } </pre> <br /> 프로그램 시작 후 최초 0이었던 s_taskIdCounter 값이 1씩 증가하는 값을 반환합니다.<br /> <br /> Status는 m_stateFlags의 값에 따라 TaskStatus 열 값을 반환합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [__DynamicallyInvokable] public TaskStatus get_Status() { int stateFlags = this.m_stateFlags; if ((stateFlags & 0x200000) != 0) { return TaskStatus.Faulted; } if ((stateFlags & 0x400000) != 0) { return TaskStatus.Canceled; } if ((stateFlags & 0x1000000) != 0) { return TaskStatus.RanToCompletion; } if ((stateFlags & 0x800000) != 0) { return TaskStatus.WaitingForChildrenToComplete; } if ((stateFlags & 0x20000) != 0) { return TaskStatus.Running; } if ((stateFlags & 0x10000) != 0) { return TaskStatus.WaitingToRun; } <span style='color: blue; font-weight: bold'>if ((stateFlags & 0x2000000) != 0) { return TaskStatus.WaitingForActivation; }</span> return TaskStatus.Created; } </pre> <br /> 디버거에 보이는 Status 값이 WaitingForActivation이므로 m_stateFlags 값에 0x2000000로 설정되어 있음을 추측하게 됩니다. 이 값은 Task 생성자에서 초기화한 값입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // System.Threading.Tasks.Task internal Task() { <span style='color: blue; font-weight: bold'>this.m_stateFlags = 0x2000400;</span> } </pre> <br /> 다음으로 DebuggerDisplayMethodDescription은,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private string DebuggerDisplayMethodDescription { get { Delegate action = (Delegate) this.m_action; if (action == null) { <span style='color: blue; font-weight: bold'>return "{null}";</span> } return action.Method.ToString(); } } </pre> <br /> 기본 생성자를 거친 객체이므로 m_action이 null이라 "{null}"로 출력된 것이 맞습니다. 마지막으로 Result는 DebuggerDisplayResultDescription 속성과 연결되어 있는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private string DebuggerDisplayResultDescription { get { if (!base.IsRanToCompletion) { return <span style='color: blue; font-weight: bold'>Environment.GetResourceString("TaskT_DebuggerNoResult");</span> } return (this.m_result); } } internal bool IsRanToCompletion { get { return ((this.m_stateFlags & 0x1600000) == 0x1000000); } } </pre> <br /> IsRanToCompletion == false이므로 Environment.GetResourceString("TaskT_DebuggerNoResult"); 코드를 실행하게 되고, 이는 mscorlib.dll의 mscorlib.resources 리소스에 담긴 "TaskT_DebuggerNoResult" Name의 값이 "{Not yet computed}"이기 때문에 출력된 것입니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
7234
(왼쪽의 숫자를 입력해야 합니다.)