Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mark task as completed

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?

like image 492
alexk Avatar asked Aug 02 '26 04:08

alexk


1 Answers

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.

like image 58
svick Avatar answered Aug 04 '26 16:08

svick



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!