I did not understand the meaning of the TaskCompletionSource<T> class. In some sources they write that it is better to return it from the method instead of the usual Task.Run() .
Is there any sense? That so that I can call await on the caller.
I did not understand the meaning of the TaskCompletionSource<T> class. In some sources they write that it is better to return it from the method instead of the usual Task.Run() .
Is there any sense? That so that I can call await on the caller.
TaskCompletionSource is the most extreme case where you cannot create a “basic” Task standard tools. Let me explain what I mean.
If you create a Task , there are two normal ways to do this.
First, if your code doesn’t wait, but works actively, for example, performs calculations (CPU-bound), you send it to the thread pool using Task.Run or its analogs.
Secondly, if you use other asynchronous operations, you create an async method in which you perform await on other asynchronous operations. .NET provides many ready-made asynchronous operations, for example, NetworkStream.ReadAsync or there Dispatcher.InvokeAsync .
But what to do if you need to create a primitive asynchronous operation yourself , which is not expressed in terms of other, ready-made asynchronous operations? How are the most internal Task methods created?
In this place, TaskCompletionSource just what you need.
For example, you want to wait for an event asynchronously. To do this, you need to turn the event into a Task . This is done something like this: we subscribe to an event, and upon its arrival we complete Task .
Task<char> WaitInput() { var tcs = new TaskCompletionSource<char>(); source.InputReceived += (o, args) => tcs.SetResult(args.Input); return tcs.Task; } A stricter version with unsubscribing, in which TaskCompletionSource used as an internal Task , in order to have time to unsubscribe after it is completed:
async Task<char> WaitInput() { var tcs = new TaskCompletionSource<char>(); SourceInputHandler handler = (o, args) => tcs.SetResult(args.Input); source.InputReceived += handler; try { return await tcs.Task; } finally { source.InputReceived -= handler; } } Another example from the real code: run and wait for the process to finish:
Task ExecuteProcess(string path) { var p = new Process() { EnableRaisingEvents = true, StartInfo = { FileName = path } }; var tcs = new TaskCompletionSource<bool>(); p.Exited += (sender, args) => { tcs.SetResult(true); p.Dispose(); }; // запуск выгружаем на пул потоков, потому что он медленный Task.Run(() => p.Start()); return tcs.Task; } Another example is taken from the DispatcherThread class . We need to wait until the flow starts, and will come to the "working" state. Usually, AutoResetEvent used for this, but it is reluctant to block while waiting, and it is much easier to use the TaskCompletionSource :
static public Task<DispatcherThread> CreateAsync() { var waitCompletionSource = new TaskCompletionSource<DispatcherThread>(); var thread = new Thread(() => { // тут могут быть любые настройки waitCompletionSource.SetResult(new DispatcherThread()); Dispatcher.Run(); }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); return waitCompletionSource.Task; } We see that in this way you can turn into Task any operation in essence.
Additional reading on the topic: TPL and Traditional. NET Framework Asynchronous Programming .
Summary: TaskCompletionSource allows you to turn into a Task even that asynchronous operation that does not provide an await -abelable interface.
On SO there was such a question. Read here . Translation of one of the best answers:
In my experiences, TaskCompletionSource great for transferring old asynchronous templates to the modern async/await template.
The most useful example I can think of is to work with Socket . It has the old APM and EAP templates, but not the awaitable Task methods that have TcpListener and TcpClient .
I personally have a few problems with the NetworkStream class and prefer the raw Socket . Being that I also like the async/await template, I created the SocketExtender extension SocketExtender , which creates several extension methods for Socket .
All of these methods use TaskCompletionSource<T> to wrap asynchronous calls as follows:
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; } I pass the Socket to the BeginAccept methods, so I get a slight performance improvement from the compiler, without requiring a local parameter to be raised.
Then the beauty of it all:
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(); Source: https://ru.stackoverflow.com/questions/780270/
All Articles