AFAIK는 모든 시점에서 속성을 통해 노출 을 완료하기 위해 해당 메서드 SetResult
또는 SetException
메서드가 호출 된다는 것을 알고 있습니다.Task<T>
Task
다시 말해, 그것은 a Task<TResult>
와 완성을 위한 생산자 역할을합니다 .
여기 예제를 보았습니다 .
Func를 비동기 적으로 실행할 수있는 방법이 필요하고 해당 작업을 나타내는 작업이있는 경우.
public static Task<T> RunAsync<T>(Func<T> function)
{
if (function == null) throw new ArgumentNullException(“function”);
var tcs = new TaskCompletionSource<T>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
T result = function();
tcs.SetResult(result);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
내가 가지고 있지 않은 경우 어느 *를 사용할 수 Task.Factory.StartNew
-하지만 않습니다 있습니다 Task.Factory.StartNew
.
질문:
누군가가 예에 의해 관련 시나리오 설명해 주시겠습니까 직접 에 TaskCompletionSource
A를하지 가상 내가하지 않아도되는 상황을 Task.Factory.StartNew
?
답변
이벤트 기반 API 만 사용할 수있는 경우 주로 사용합니다 ( 예 : Windows Phone 8 소켓 ).
public Task<Args> SomeApiWrapper()
{
TaskCompletionSource<Args> tcs = new TaskCompletionSource<Args>();
var obj = new SomeApi();
// will get raised, when the work is done
obj.Done += (args) =>
{
// this will notify the caller
// of the SomeApiWrapper that
// the task just completed
tcs.SetResult(args);
}
// start the work
obj.Do();
return tcs.Task;
}
따라서 C # 5 async
키워드 와 함께 사용할 때 특히 유용 합니다.
답변
내 경험에 따르면 TaskCompletionSource
오래된 비동기 패턴을 현대 async/await
패턴 에 래핑하는 데 좋습니다 .
내가 생각할 수있는 가장 유리한 예는와 작업 할 때입니다 Socket
. 그것은 기존의 APM 및 EAP 패턴 아니지만이 awaitable Task
방법 TcpListener
및 TcpClient
이를.
나는 개인적으로 NetworkStream
수업에 몇 가지 문제가 있으며 생식을 선호합니다 Socket
. async/await
패턴 도 좋아하기 때문에에 SocketExtender
대한 몇 가지 확장 메서드를 만드는 확장 클래스 를 만들었습니다 Socket
.
이러한 모든 메소드는 다음 TaskCompletionSource<T>
과 같이 비동기 호출을 랩핑하는 데 사용합니다 .
public static Task<Socket> AcceptAsync(this Socket socket)
{
if (socket == null)
throw new ArgumentNullException("socket");
var tcs = new TaskCompletionSource<Socket>();
socket.BeginAccept(asyncResult =>
{
try
{
var s = asyncResult.AsyncState as Socket;
var client = s.EndAccept(asyncResult);
tcs.SetResult(client);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
}, socket);
return tcs.Task;
}
나는 통과 socket
에 BeginAccept
내가 컴파일러는 로컬 매개 변수를 게양하지 않아도에서 약간의 성능 향상을 얻을 수 있도록하는 것이 방법.
그런 다음 그 아름다움
var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(new IPEndPoint(IPAddress.Loopback, 2610));
listener.Listen(10);
var client = await listener.AcceptAsync();
답변
나에게있어, 사용에 대한 고전적인 시나리오 TaskCompletionSource
는 내 방법이 시간이 많이 걸리는 작업을 수행 할 필요 가 없을 때입니다. 우리가 할 수있는 일은 새 스레드를 사용하려는 특정 사례를 선택하는 것입니다.
이에 대한 좋은 예는 캐시를 사용하는 경우입니다. GetResourceAsync
요청 된 자원을 캐시에서 찾고 TaskCompletionSource
자원을 찾은 경우 새 스레드를 사용하지 않고 한 번에 리턴 하는 메소드 를 가질 수 있습니다 . 리소스를 찾을 수없는 경우에만 새 스레드를 사용하고을 사용하여 검색하려고합니다 Task.Run()
.
코드 예제는 여기에서 볼 수 있습니다 : 작업을 사용하여 조건부로 코드를 비동기 적으로 실행하는 방법
답변
에서 이 블로그 게시물 , 레위 Botelho은을 사용하는 방법에 대해 설명합니다 TaskCompletionSource
당신이 그것을 실행하고 종료를 기다릴 수 있도록 프로세스에 대한 비동기 래퍼를 작성.
public static Task RunProcessAsync(string processPath)
{
var tcs = new TaskCompletionSource<object>();
var process = new Process
{
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo(processPath)
{
RedirectStandardError = true,
UseShellExecute = false
}
};
process.Exited += (sender, args) =>
{
if (process.ExitCode != 0)
{
var errorMessage = process.StandardError.ReadToEnd();
tcs.SetException(new InvalidOperationException("The process did not exit correctly. " +
"The corresponding error message was: " + errorMessage));
}
else
{
tcs.SetResult(null);
}
process.Dispose();
};
process.Start();
return tcs.Task;
}
그리고 그 사용법
await RunProcessAsync("myexecutable.exe");
답변
아무도 언급하지 않은 것처럼 보이지만 단위 테스트도 실제 생활로 충분히 간주 될 수 있다고 생각 합니다.
TaskCompletionSource
비동기 메서드로 종속성을 조롱 할 때 유용하다는 것을 알았습니다 .
테스트중인 실제 프로그램에서 :
public interface IEntityFacade
{
Task<Entity> GetByIdAsync(string id);
}
단위 테스트에서 :
// set up mock dependency (here with NSubstitute)
TaskCompletionSource<Entity> queryTaskDriver = new TaskCompletionSource<Entity>();
IEntityFacade entityFacade = Substitute.For<IEntityFacade>();
entityFacade.GetByIdAsync(Arg.Any<string>()).Returns(queryTaskDriver.Task);
// later on, in the "Act" phase
private void When_Task_Completes_Successfully()
{
queryTaskDriver.SetResult(someExpectedEntity);
// ...
}
private void When_Task_Gives_Error()
{
queryTaskDriver.SetException(someExpectedException);
// ...
}
결국,이 TaskCompletionSource 사용법은 “코드를 실행하지 않는 Task 객체”의 또 다른 사례로 보입니다.
답변
TaskCompletionSource 는 코드를 실행하지 않는 Task 개체 를 만드는 데 사용됩니다 . 실제 시나리오에서 TaskCompletionSource 는 I / O 바운드 작업에 이상적입니다. 이렇게하면 작업 기간 동안 스레드를 차단하지 않고도 작업의 모든 이점 (예 : 반환 값, 연속 등)을 얻을 수 있습니다. “함수”가 I / O 바운드 작업 인 경우 새 Task를 사용하여 스레드를 차단하지 않는 것이 좋습니다 . 대신 TaskCompletionSource를 사용 하여 I / O 바운드 작업이 완료되거나 오류가 발생한시기 만 표시하는 슬레이브 작업을 만들 수 있습니다.
답변
이 게시물에는 “.NET을 사용한 병렬 프로그래밍”블로그 의 적절한 설명이 포함 된 실제 예가 있습니다. 당신은 정말로 그것을 읽어야하지만, 여기 요약이 있습니다.
블로그 게시물은 다음 두 가지 구현을 보여줍니다.
“지연된 작업을 생성하는 팩토리 방법으로, 일부 사용자 제공 시간 초과가 발생할 때까지 실제로 예약되지 않는 작업입니다.”
표시된 첫 번째 구현 Task<>
은 두 가지 주요 결함을 기반으로 하고 있습니다. 두 번째 구현 포스트는를 사용하여이를 완화합니다 TaskCompletionSource<>
.
두 번째 구현은 다음과 같습니다.
public static Task StartNewDelayed(int millisecondsDelay, Action action)
{
// Validate arguments
if (millisecondsDelay < 0)
throw new ArgumentOutOfRangeException("millisecondsDelay");
if (action == null) throw new ArgumentNullException("action");
// Create a trigger used to start the task
var tcs = new TaskCompletionSource<object>();
// Start a timer that will trigger it
var timer = new Timer(
_ => tcs.SetResult(null), null, millisecondsDelay, Timeout.Infinite);
// Create and return a task that will be scheduled when the trigger fires.
return tcs.Task.ContinueWith(_ =>
{
timer.Dispose();
action();
});
}