I’m implementing a client protocol MyProtocol over TCP/IP. The protocol’s Connect() method should have a signature similar to that of TcpClient.ConnectAsync() – that is, it should return a Task:
Task MyProtocol.Connect (…);
This method (MyProtocol.Connect()) should make asynchronous TCP/IP connection (via TcpClient.ConnectAsync()), return a non-completed task T and then periodically send a certain message M to the server – again asynchronously (via NetworkStream.WriteAsync()). When a certain response R is received from the server – again asynchronously (via NetworkStream.ReadAsync()), MyProtocol.Connect() should complete the task T.
I’m doing the following:
// Client of the protocol:
var task = myProtocol.Connect(); // asynchronous call, we don’t want to wait until connected
task.ContinueWith(t =>
{
// Connected – doing an OnConnected stuff
…
});
// MyProtocol.Connect() implementation:
public class MyProtocol
{
private Task connectTask;
public Task Connect(…)
{
var tcpIpConnectTask = mTcpIpProtocol.Connect(…);
tcpIpConnectTask.ContinueWith(t =>
{
connectTask = new Task();
}
return connectTask;
}
}
Periodical sending of message M to the server obviously has to be done through a timer. Once the response R is received from the server asynchronously, connectTask must be marked as completed, and I don’t see ways of doing that.
Well, strictly speaking, I have managed to mark connectTask as completed; I’m wrapping it in TaskCompletionSource<bool> and use TaskCompletionSource.SetResult(true).
However I’m wondering if this is the only, let alone the best way of accomplishing what I need? I especially don’t like the fact that TaskCompletionSource<> has to have a non-void task result type (I used bool), i.e. there’s no non-generic version.
Until TPL arrived, we had our own similar framework and a method Task.NotifyCompleted(), so we could create a Task in one place, and mark it as completed in another place – all this was working asynchronously. But all I’ve read about tasks in TPL seems to imply that a Task only completes if its delegate runs to the last line…
Or am I missing something simple?
Since you're already using .Net 4.5, you should use C# 5.0 async-await, which is meant exactly for this kind of situations. The code could look something like (somewhat pseudo-codish):
public Task ConnectAsync()
{
await ClientConnectAsync();
while (true)
{
await SendMessageAsync();
var response = await ReceiveMessageAsync();
if (response == R)
return;
await Task.Delay(period);
}
}
To actually answer your questions:
However I’m wondering if this is the only, let alone the best way of accomplishing what I need?
If you don't want to use async-await, then yes, TaskCompletionSource is the only general-purpose way of doing this.
I especially don’t like the fact that
TaskCompletionSource<>has to have a non-void task result type (I used bool), i.e. there’s no non-generic version.
That is somewhat annoying, but it doesn't really matter, since Task<T> inherits from Task.
But all I’ve read about tasks in TPL seems to imply that a Task only completes if its delegate runs to the last line […]
That's true for Tasks that do have a delegate to run (those can be created using Task.Run(), Task.Factory.StartNew() or new Task()). But it doesn't apply to Tasks with no delegate, those can be created using async-await or TaskCompletionSource.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With