111

백그라운드 스레드에서 실행되는 작업을 트리거하고 싶습니다. 작업 완료를 기다리고 싶지 않습니다.

.net 3.5에서이 작업을 수행했을 것입니다.

ThreadPool.QueueUserWorkItem(d => { DoSomething(); });

넷에서 TPL은 제안 된 방법입니다. 추천 된 일반적인 패턴은 다음과 같습니다.

Task.Factory.StartNew(() => { DoSomething(); });

그러나, 그StartNew()메서드는Task구현하는 객체IDisposable. 이 이 패턴을 추천하는 사람들이 간과하는 것처럼 보인다. MSDN 문서Task.Dispose()방법 말한다 :

"작업에 대한 마지막 참조를 해제하기 전에 항상 Dispose에 전화하십시오."

작업이 완료 될 때까지 dispose를 호출 할 수 없으므로 주 스레드 대기 및 호출 처 리를 수행하면 처음부터 백그라운드 스레드에서 수행 지점을 무력화 할 수 있습니다. 또한 정리에 사용할 수있는 완료 / 완료 이벤트가없는 것 같습니다.

Task 클래스의 MSDN 페이지는 이에 대해 언급하지 않고 "Pro C #2010 ..."책은 동일한 패턴을 권장하고 작업 처리에 대해서는 언급하지 않습니다.

내가 방금 종료자를 남겨두면 최종적으로 그것을 잡을 것입니다. 그러나 이것은 많은 불 &을하고있을 때 되돌아 와서 물러 설 것입니다. 이와 같은 작업을 잊어 버리면 파이널 라이저 스레드가 당황합니까?

그래서 내 질문은 :

  • 전화하지 않는 것이 맞습니까?Dispose()~에Task이 경우 클래스? 그렇다면 왜 위험과 결과가 있습니까?
  • 이에 대해 설명하는 문서가 있습니까?
  • 아니면 적절한 처분 방법이 있습니까?Task내가 놓친 물건?
  • 또는 화재 및 진압의 또 다른 방법이 있습니까? TPL로 작업 잊어 버리시겠습니까?


3 답변


95

이것에 대한 토론이 있습니다.MSDN 포럼.

Microsoft pfx 팀원 인 Stephen Toub은 다음과 같이 말합니다.

작업으로 인해 Task.Dispose가 존재합니다.   잠재적으로 이벤트 핸들을 래핑   작업을 기다릴 때 사용됩니다.   완료, 대기중인 경우   스레드가 실제로 차단해야합니다 (   회전이나 잠재적으로   대기중인 작업 실행).   네가하는 모든 일이   그 이벤트 핸들은   결코 할당되지 않는다.

...

상황을 처리하기 위해 최종 결정에 의존하는 것이 좋습니다.

업데이트 (2012 년 10 월)

Stephen Toub은 제목이 블로그를 게시했습니다.할 일 목록을 처리해야합니까?좀 더 자세한 내용을 제공하고 .Net 4.5의 개선점을 설명합니다.

요약하면 다음을 처리 할 필요가 없습니다.Task개체의 99 %.

개체를 처리하는 주된 이유는 두 가지입니다. 관리되지 않는 리소스를 적시에 결정적으로 확보하고 개체의 최종 자 도구를 실행하는 데 드는 비용을 피하십시오. 이들 중 어느 것도Task대부분의 시간 :

  1. .Net 4.5부터, 유일한 시간은Task내부 대기 핸들을 할당합니다.Task개체)는 명시 적으로IAsyncResult.AsyncWaitHandle~의Task, 및
  2. 그만큼Task객체 자체에는 파이널 라이저가 없습니다. 핸들 자체는 파이널 라이저를 사용하여 객체에 래핑되므로 할당되어 있지 않으면 실행할 파이널 라이저가 없습니다.


  • 고마워, 흥미 롭 군. 그것은 비록 MSDN 문서에 반대한다. MS 또는 .net 팀의 공식적인 단어가 수용 가능한 코드인지 확인하십시오. 또한 그 토론의 마지막에서 "향후 버전에서 구현이 변경되면 어떻게 될 것인가?" - Simon P Stevens
  • 사실, 그 스레드의 답변자가 실제로 Microsoft에서 일하는 것으로 나타났습니다 (pfx 팀에있는 것처럼 보입니다), 이것이 공식 답변 일 것이라고 생각합니다. 그러나 모든 경우에 작동하지 않는 바닥에 대한 제안이 있습니다. 누출 가능성이있는 경우 ThreadPool.QueueUserWorkItem으로 되 돌리는 것이 안전하다는 것을 알고 있습니다. - Simon P Stevens
  • 네, 전화하지 않을 수도있는 Dispose가 있다는 것은 매우 이상합니다. 여기 샘플을 보시면msdn.microsoft.com/en-us/library/dd537610.aspx그리고 여기msdn.microsoft.com/en-us/library/dd537609.aspx그들은 업무를 처리하지 않습니다. 그러나 MSDN의 코드 샘플은 때로는 매우 나쁜 기술을 보여줍니다. 또한이 사람은 Microsoft의 질문에 대답했습니다. - Kirill Muzykov
  • @ 사이먼 : (1) 귀하가 인용 한 MSDN 의사는 일반적인 조언이며 구체적인 사례에는보다 구체적인 조언이 있습니다 (예 :EndInvoke사용하는 경우 WinForms에서BeginInvokeUI 스레드에서 코드를 실행). (2) Stephen Toub은 PFX를 효과적으로 사용하는 데 대한 일반 연사로 잘 알려져 있습니다 (예 : onchannel9.msdn.com), 그래서 누군가가 좋은 지침을 줄 수 있다면 그는 그것입니다. 두 번째 단락을 주목하십시오 : 최종 자에게 물건을 남기는 것이 더 낫습니다. - Richard
  • 이것은 너무 복잡합니다 ... - UserControl

14

이것은 Thread 클래스와 같은 종류의 문제이다. 5 개의 운영 체제 핸들을 사용하지만 IDisposable은 구현하지 않습니다. 원래 디자이너의 결정에 따라 Dispose () 메소드를 호출하는 합리적인 방법은 거의 없습니다. 먼저 Join ()을 호출해야합니다.

Task 클래스는 하나의 핸들을 내부 수동 재설정 이벤트로 추가합니다. 가장 저렴한 운영 체제 리소스가 있습니다. 물론 Dispose () 메서드는 Thread가 소비하는 5 개의 핸들이 아니라 하나의 이벤트 핸들 만 릴리스 할 수 있습니다.그래, 신경 안써..

작업의 IsFaulted 속성에 관심을 가져야한다는 것을 유의하십시오. 그것은 꽤 추악한 주제입니다, 당신은 이것에 대해 더 많은 것을 읽을 수 있습니다.MSDN Library 기사. 이 문제를 적절하게 처리하면 코드에서 작업을 처리하기에 좋은 자리를 가져야합니다.


  • 고마워 한스. 좋은 비교 거기. - Simon P Stevens
  • 하지만 작업을 수행 할 수 없습니다.Thread대부분의 경우 ThreadPool을 사용합니다. - svick

-1

이 게시물에 표시된 기술에 누군가가 무게가 나는 것을보고 싶습니다.C #에서 형식 보증 된 비동기 대리자 호출

간단한 확장 메소드가 태스크와 상호 작용하는 모든 사소한 경우를 처리하고 태스크를 처리 할 수있는 것처럼 보입니다.

public static void FireAndForget<T>(this Action<T> act,T arg1)
{
    var tsk = Task.Factory.StartNew( ()=> act(arg1),
                                     TaskCreationOptions.LongRunning);
    tsk.ContinueWith(cnt => cnt.Dispose());
}


  • 물론 그것은 처분하지 못합니다.Task에 의해 반환 된 인스턴스ContinueWith,하지만 Stephen Toub의 인용문은 받아 들여진 대답입니다. 아무 것도 작업 대기를 차단하지 않으면 처분 할 것이 없습니다. - Richard
  • 리차드가 언급 한대로 ContinueWith (...)는 두 번째 Task 객체를 반환하고 그 객체는 처리되지 않습니다. - Simon P Stevens
  • 따라서 ContinueWith 코드는 이전 작업을 처리하기 위해 다른 작업을 생성하게되므로 실제로 코드를 사용하는 것보다 더 나쁩니다. 이런 식의 의미에서 기본적으로 차단 대기를이 코드 블록에 도입 할 수 없다는 것 외에도 전달 된 작업 대리인이 작업 자체를 수정하려고 시도했을 때 올바른 것입니까? - Chris Marisic
  • 두 번째 작업을 처리하기 위해 약간 까다로운 방법으로 람다가 변수를 포착하는 방법을 사용할 수 있습니다.Task disper = null; disper = tsk.ContinueWith(cnt => { cnt.Dispose(); disper.Dispose(); }); - Gideon Engelberth
  • 겉으로보기 엔 작동해야 할 @GideonEngelberth. Disper는 GC에 의해 폐기되어서는 안되기 때문에, 참조가 여전히 유효하다고 가정 할 때, 람다가 처리 할 때까지 유효한 상태를 유지해야합니다. 어쩌면 빈 try / catch가 필요합니까? - Chris Marisic

연결된 질문


관련된 질문

최근 질문