Question

I have a parrallel task that writes some data in a file

using (var lFileStream = File.Open(aDestinationFile, shouldResume ? FileMode.Append : FileMode.Create, FileAccess.Write))
{
    try
    {
        while ((lCount = lDownloadStream.Read(lBuf, 0, lBuf.Length)) > 0)
        {
            lFileStream.Write(lBuf, 0, lCount);
        }
    }
    finally
    {
        lFileStream.Close();
    }
}

I am launching the task by:

Task.Factory.StartNew(() => 
                {
                    try
                    {
                        myMethod();
                    }
                    catch (Exception ex)
                    {
                        Log.Exc(ex);
                    }
                }
            , _resetEvent.Token);

under some condition I have to terminate the task:

_resetEvent.Cancel(false);
try
{
    _task.Dispose();
}
catch
{
}

after the task stop I am starting a new task, but when i am try to aceess the file that was used i am getting:

The process cannot access the file 'bla-bla' because it is being used by another process.

how can I "kill" the task properly?

Was it helpful?

Solution

When you pass a CancellationToken in to a task all that does is prevent the task from starting if the token is canceled by the time the task scheduler gets around to starting the task. It has no effect on the task once the task has already started up.

What you need to do is "Cooperative Cancelation", you need to check the token inside your code that is running inside the task and have it cancel what it is doing. The easiest way to do that is just pass the CancelationToken in to the method itself then run the function ThrowIfCancellationRequested(); and your program will throw a OperationCanceledException and do whatever cleanup you have set up to handle that exception.

private void myMethod(CancellationToken token)
{
    using (var lFileStream = File.Open(aDestinationFile, shouldResume ? FileMode.Append : FileMode.Create, FileAccess.Write))
    {
        //The try-finally was not nessessary, Dispose() will call Close() for you.
        while ((lCount = lDownloadStream.Read(lBuf, 0, lBuf.Length)) > 0)
        {
            token.ThrowIfCancellationRequested();
            lFileStream.Write(lBuf, 0, lCount);
        }
    }
}

Task.Factory.StartNew(() => 
                {
                    try
                    {
                        myMethod(_resetEvent.Token);
                    }
                    catch (Exception ex)
                    {
                        //If the task was canceled we don't need to log the exception.
                        if(!ex is OperationCanceledException)
                            Log.Exc(ex);
                    }
                }
            , _resetEvent.Token);

To reduce refactoring you could do the following to your writer method

//All your old code can still call this method.
public void myMethod()
{
    myMethod(CancellationToken.None); //Call the overload with a empty token.
}

//New code that needs to cancel the operation can call this method.
public void myMethod(CancellationToken token)
{
     //Slightly modified old Writer code that uses the CancelationToken inside any loops or in between any long running operations that can't be interrupted.
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top