TaskCompletionSource는 언제해야합니까 사용 되나요?
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
는 내 방법이 시간이 많이 걸리는 작업을 수행 할 필요 가 없을 때입니다. 우리가 할 수있는 일은 새 스레드를 사용하려는 특정 사례를 선택하는 것입니다.
A good example for this is when you use a cache. You can have a GetResourceAsync
method, which looks in the cache for the requested resource and returns at once (without using a new thread, by using TaskCompletionSource
) if the resource was found. Only if the resource wasn't found, we'd like to use a new thread and retrieve it using Task.Run()
.
A code example can be seen here: How to conditionally run a code asynchonously using tasks
In this blog post, Levi Botelho describes how to use the TaskCompletionSource
to write an asynchronous wrapper for a Process such that you can launch it and await its termination.
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;
}
and its usage
await RunProcessAsync("myexecutable.exe");
TaskCompletionSource is used to create Task objects that don't execute code. In Real World Scenarios TaskCompletionSource is ideal for I/O bound operations. This way you get all the benefits of tasks (e.g. return values, continuations etc) without blocking a thread for the duration of the operation. If your "function" is an IO bound operation it isn't recommended to block a thread using a new Task. Instead using TaskCompletionSource you can create a slave task to just indicate when your I/O bound operation finishes or faults.
It looks like no one mentioned, but I guess unit tests too can be considered real life enough.
I find TaskCompletionSource
to be useful when mocking a dependency with an async method.
In actual program under test:
public interface IEntityFacade
{
Task<Entity> GetByIdAsync(string id);
}
In unit tests:
// 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);
// ...
}
After all, this usage of TaskCompletionSource seems another case of "a Task object that does not execute code".
There's a real world example with a decent explanation in this post from the "Parallel Programming with .NET" blog. You really should read it, but here's a summary anyway.
The blog post shows two implementations for:
"a factory method for creating “delayed” tasks, ones that won’t actually be scheduled until some user-supplied timeout has occurred."
The first implementation shown is based on Task<>
and has two major flaws. The second implementation post goes on to mitigate these by using TaskCompletionSource<>
.
Here's that second implementation:
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();
});
}
I real world scenario where I have used TaskCompletionSource
is when implementing a download queue. In my case if the user starts 100 downloads I don't want to fire them all off at once and so instead of returning a strated task I return a task attached to TaskCompletionSource
. Once the download gets completed the thread that is working the queue completes the task.
The key concept here is that I am decoupling when a client asks for a task to be started from when it actually gets started. In this case because I don't want the client to have to deal with resource management.
note that you can use async/await in .net 4 as long as you are using a C# 5 compiler (VS 2012+) see here for more details.
This may be oversimplifying things but the TaskCompletion source allows one to await on an event. Since the tcs.SetResult is only set once the event occurs, the caller can await on the task.
Watch this video for more insights:
I've used TaskCompletionSource
to run a Task until it is cancelled. In this case it's a ServiceBus subscriber that I normally want to run for as long as the application runs.
public async Task RunUntilCancellation(
CancellationToken cancellationToken,
Func<Task> onCancel)
{
var doneReceiving = new TaskCompletionSource<bool>();
cancellationToken.Register(
async () =>
{
await onCancel();
doneReceiving.SetResult(true); // Signal to quit message listener
});
await doneReceiving.Task.ConfigureAwait(false); // Listen until quit signal is received.
}
참고URL : https://stackoverflow.com/questions/15316613/when-should-taskcompletionsourcet-be-used
'Programing' 카테고리의 다른 글
파일의 시작 부분에 텍스트를 삽입하는 방법? (0) | 2020.05.15 |
---|---|
Virtualenvs의 깨진 참조 (0) | 2020.05.15 |
(Java) 패키지 구성에 대한 모범 사례가 있습니까? (0) | 2020.05.15 |
Xcode : 일반 언어로 된 목표와 체계는 무엇입니까? (0) | 2020.05.15 |
중첩 된 객체를 쿼리하는 방법? (0) | 2020.05.15 |