C# 终止使用文件流的任务的正确方法是什么

C# 终止使用文件流的任务的正确方法是什么,c#,file-io,task,cancellation,C#,File Io,Task,Cancellation,我有一个并行任务,在文件中写入一些数据 using (var lFileStream = File.Open(aDestinationFile, shouldResume ? FileMode.Append : FileMode.Create, FileAccess.Write)) { try { while ((lCount = lDownloadStream.Read(lBuf, 0, lBuf.Length)) > 0) {

我有一个并行任务,在文件中写入一些数据

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();
    }
}
我通过以下方式启动该任务:

Task.Factory.StartNew(() => 
                {
                    try
                    {
                        myMethod();
                    }
                    catch (Exception ex)
                    {
                        Log.Exc(ex);
                    }
                }
            , _resetEvent.Token);
在某些情况下,我必须终止任务:

_resetEvent.Cancel(false);
try
{
    _task.Dispose();
}
catch
{
}
任务停止后,我开始一项新任务,但当我尝试访问所使用的文件时,我得到:

进程无法访问文件“bla bla”,因为它正在使用中 通过另一个过程


如何才能正确地“终止”任务?

当您将
取消令牌
传递给任务时,如果在任务计划程序开始启动任务时令牌被取消,那么所做的就是阻止任务启动。一旦任务已经启动,它对任务没有影响

您需要做的是“协作取消”,您需要检查在任务中运行的代码中的令牌,并让它取消正在执行的操作。最简单的方法是将CancelationToken传递给方法本身,然后运行函数ThrowIfCancellationRequested()程序将抛出一个
操作canceledException
,并执行为处理该异常而设置的任何清理操作

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);

为了减少重构,可以对writer方法执行以下操作

//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.
}

CancellationToken
传递到任务时,如果在任务计划程序开始启动任务时取消了令牌,那么所做的就是阻止任务启动。一旦任务已经启动,它对任务没有影响

您需要做的是“协作取消”,您需要检查在任务中运行的代码中的令牌,并让它取消正在执行的操作。最简单的方法是将CancelationToken传递给方法本身,然后运行函数ThrowIfCancellationRequested()程序将抛出一个
操作canceledException
,并执行为处理该异常而设置的任何清理操作

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);

为了减少重构,可以对writer方法执行以下操作

//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.
}

您不需要尝试使用block calls
Close()最终阻止从
调用的
Dispose()
给你。@ScottChamberlain纠正你说的:
使用
不会调用
Close
它会调用
Dispose
,然后调用
Close
@SriramSakthivel这就是我想说的(感谢你让它变得不那么复杂),你不需要尝试最终阻止
Dispose()
从您的
调用,使用
块调用
Close()
。@ScottChamberlain纠正您所说的:
使用
不会调用
Close
它会调用
Dispose
然后调用
Close
@SriramSakthivel这正是我想说的(感谢您让它不那么复杂)我做对了吗“合作癌变”是唯一的方法?我提出这个问题,因为“writer”方法是我的解决方案中其他方法也使用的代码。这就是为什么我想避免重构是的,writer方法必须“配合”取消,否则必须等待写入完成,然后系统才会“关闭”。查看“我的更新”以简化重构,您只需修改writer函数和任何要支持的函数即可取消操作,writer函数的所有其他用途都可以保持不变。为什么要使用
Task.Factory.StartNew
。您希望使用
Stream.BeginWrite
Stream.EndWrite
以及
TaskFactory.fromsync
。如果您使用的是.NET 4.5或.NET 4.0,为了使它更具响应性,您可以让代码使用async/wait并替换
无效流。Write(byte[],int,int)
调用
任务流。WriteAsync(byte[],int,int,CancelationToken)
并将令牌传递给write函数。这将使您的函数在令牌发出后立即停止,而不是等待
写入(
返回。@ScottChamberlain
Stream.WriteAsync
是4.5而不是4.0我是否正确了“合作cancalation”是唯一的方法?我问这个问题,因为“写入者”方法是我的解决方案中其他方法也使用的代码。这就是为什么我希望避免重构是的,writer方法必须“配合”取消,否则必须等待写入完成,然后才能“关闭”通过系统。请参阅我的更新以简化重构,您只需修改writer函数和任何支持取消操作的函数,writer函数的所有其他用途都可以保持不变。为什么要使用
Task.Factory.StartNew
。您要使用
Stream.BeginWrite
e> Stream.EndWrite
TaskFactory.fromsync
一起使用。如果您使用的是.NET 4.5或.NET 4.0,为了使其更具响应性,您可以让代码使用async/wait并替换
无效的Stream.Write(byte[],int,int)
调用
任务流.WriteAsync(byte[],int,int,CancelationToken)
并将令牌传递给写入函数。这将使您的函数在令牌发出后立即停止,而不是等待
写入(
返回。@ScottChamberlain
Stream.WriteAsync
为4.5而非4.0