백그라운드 스레드에서 실행되도록 작업을 트리거하고 싶습니다. 작업 완료를 기다리고 싶지 않습니다.
.net 3.5에서는이 작업을 수행했을 것입니다.
ThreadPool.QueueUserWorkItem(d => { DoSomething(); });
.net 4에서는 TPL이 권장되는 방법입니다. 내가 본 일반적인 패턴은 다음과 같습니다.
Task.Factory.StartNew(() => { DoSomething(); });
그러나, StartNew()
방법은 반환 Task
되는 구현 객체 IDisposable
. 이 패턴을 추천하는 사람들은 이것을 간과하는 것 같습니다. Task.Dispose()
방법 에 대한 MSDN 설명서에는 다음과 같은 내용이 있습니다.
“작업에 대한 마지막 참조를 해제하기 전에 항상 Dispose를 호출하십시오.”
작업이 완료 될 때까지 작업에 대해 dispose를 호출 할 수 없으므로 주 스레드가 대기하고 dispose를 호출하면 처음에 백그라운드 스레드에서 수행하는 요점이 무너집니다. 또한 정리에 사용할 수있는 완료 / 종료 된 이벤트가없는 것 같습니다.
Task 클래스의 MSDN 페이지는 이에 대해 언급하지 않으며 “Pro C # 2010 …”책은 동일한 패턴을 권장하며 작업 처리에 대해서는 언급하지 않습니다.
내가 그냥두면 파이널 라이저가 결국 그것을 잡을 것이라는 것을 알고 있지만, 내가 많은 불을 지르고 이와 같은 작업을 잊어 버리고 파이널 라이저 스레드가 압도 당할 때 이것이 돌아와서 나를 물게 될까요?
그래서 내 질문은 다음과 같습니다.
- 그것은 호출하지 받아 들일
Dispose()
온Task
이 경우 클래스? 그렇다면 왜 그리고 위험 / 결과가 있습니까? - 이에 대해 설명하는 문서가 있습니까?
- 아니면
Task
내가 놓친 물건을 처분하는 적절한 방법이 있습니까? - 아니면 TPL로 작업을 수행하고 잊어 버리는 다른 방법이 있습니까?
답변
MSDN 포럼에서 이에 대한 토론이 있습니다 .
Microsoft pfx 팀의 구성원 인 Stephen Toub는 다음과 같이 말합니다.
Task.Dispose는 대기중인 스레드가 실제로 차단해야하는 경우 (대기중인 작업을 회전하거나 잠재적으로 실행하는 것과는 반대로) 작업이 완료 될 때까지 대기 할 때 사용되는 이벤트 핸들을 잠재적으로 래핑하기 때문에 존재합니다. 당신이하고있는 모든 것이 연속을 사용하는 것이라면, 그 이벤트 핸들은 절대 할당되지 않을 것입니다
…
일을 처리하기 위해 마무리에 의존하는 것이 더 낫습니다.
업데이트 (2012 년 10 월)
Stephen Toub는 Do I need to dispose of Tasks? 라는 제목의 블로그를 게시했습니다 . 좀 더 자세한 정보를 제공하고 .Net 4.5의 개선 사항을 설명합니다.
요약 : Task
99 %의 시간 동안 개체 를 처리 할 필요가 없습니다 .
개체를 폐기하는 두 가지 주된 이유는 관리되지 않는 리소스를 적시에 결정적인 방식으로 확보하고 개체의 종료자를 실행하는 비용을 피하는 것입니다. Task
대부분의 경우 다음 중 어느 것도 적용되지 않습니다 .
- .Net 4.5부터 a
Task
가 내부 대기 핸들 (Task
객체 의 유일한 관리되지 않는 리소스)을 할당 하는 유일한 시간IAsyncResult.AsyncWaitHandle
은Task
, 및 Task
객체 자체는 종료자가 없습니다; 핸들 자체는 종료자가있는 객체로 둘러싸여 있으므로 할당되지 않는 한 실행할 종료자가 없습니다.
답변
이것은 Thread 클래스와 같은 종류의 문제입니다. 5 개의 운영 체제 핸들을 사용하지만 IDisposable을 구현하지 않습니다. 원래 디자이너의 좋은 결정이지만 Dispose () 메서드를 호출하는 합리적인 방법은 거의 없습니다. 먼저 Join ()을 호출해야합니다.
Task 클래스는 여기에 하나의 핸들, 즉 내부 수동 재설정 이벤트를 추가합니다. 가장 저렴한 운영 체제 리소스입니다. 물론 Dispose () 메서드는 Thread가 사용하는 5 개의 핸들이 아닌 하나의 이벤트 핸들 만 해제 할 수 있습니다. 그래, 귀찮게 하지마 .
작업의 IsFaulted 속성에 관심이 있어야합니다. 매우 추악한 주제 이며이 MSDN Library 기사 에서 이에 대한 자세한 내용을 읽을 수 있습니다 . 이 문제를 제대로 처리하고 나면 코드에서 작업을 처리 할 수있는 좋은 위치가 있어야합니다.
답변
누군가가이 게시물에 표시된 기술에 무게를두고보고 싶습니다. C #에서 Typesafe fire-and-forget 비동기 델리게이트 호출
간단한 확장 메서드가 작업과 상호 작용하는 모든 사소한 경우를 처리하고 dispose를 호출 할 수있는 것처럼 보입니다.
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());
}