How to await Tasks kicked off in Dispose

时间:2016-04-04 17:24:56

标签: c# .net async-await .net-4.5 c#-5.0

I have a general question about how to keep track of an asynchronous task that is started when an object is disposed in .Net. I have a class designed around Stephen Cleary's Dispose means cancel paradigm. The problem is since Dispose is a void there is no way to keep track of that task directly.

I created another static class that has a list of tasks that allows me to do something like this at the end of my program to make sure all dispose tasks finish before the program exits.

    public async Task WaitForAllDanglingTasks(CancellationToken token)
    {
        while (_dangling.Count > 0)
        {
            token.ThrowIfCancellationRequested();
            var t = await Task.WhenAny(_dangling.ToArray()).DetachContext();
            await _lock.WaitAsync(token).DetachContext();

            try
            {
                _dangling.Remove(t);
            }
            finally
            {
                _lock.Release();
            }
        }

    }

Is this a good approach? Is there something better or is this even needed?

EDIT: Originally my problem was compounded by continuation task not executing and later I found that was due to a deadlock caused by my use of the SerialPort library. Please ignore all that follows. I left it so the comments make sense.

The problem is this blocks forever because most of my disposal tasks never run(usually one will run). Unfortunately I cannot share the code due to IP. But basically I have a socket that creates an object responsible for a long running RX task that polls the connection. Here is the pseudo code.

// This is in the Service class. ServiceTask is a public member that
// allows the socket to continue with other cleanup when it is done.
async Task StartService(args, token)
{
    ServiceTask = Task.Run(async () => await LongRunning(args, token))
                        .continuewith(t => cleanup(args));
    ServiceTask.TrackDisposal(); //adds to disposal Task list
    await LongRunningReady(token);
}

async Task LongRunning(args, token)
{
    using( var combined = CreateLinkedSource(_dispose.token, token))
    {
        DoWork(args);
    }
}

void Dispose()
{
    _dispose.Cancel();
}

I also read on Stephen Cleary's blog that he also suggests using await instead of ContinueWith. I do use await in most cases but with the long running task using Continuewith I can just call cancel on a token source and then the task ends naturally and then continues with the clean up. With await I would have to store some state in that class to clean up the stuff after awaiting for the ServiceTask variable.

I understand without the code this will be hard to diagnose. But I want to know if there is anything glaringly obvious that I am doing wrong that would cause the continuation tasks to not run.

It is also worth mentioning I am running from a console application and am using the AsyncContext from AsyncEx to support the async code. As well as, ManyConsole for the command line parsing and presentation.

0 个答案:

没有答案