2015-06-06 176 views
1

我正在使用System.IO.File.Copy将文件从远程共享复制到本地系统。如果副本花费太长时间,我该如何实现超时?如何在使用File.Copy时实现超时?

+0

也许比超时更好的是异步工作:[异步文件复制/移动在C#](http://stackoverflow.com/questions/882686/asynchronous-file-copy-move- in-c-sharp) –

+1

https://msdn.microsoft.com/zh-cn/magazine/cc163851.aspx –

回答

1

例如,它可以做到用这种方式async - await模式:

Task timeoutTask = Task.Delay(TimeSpan.FromSeconds(10)); 

// I use a completion source to set File.Copy thread from its own 
// thread, and use it later to abort it if needed 
TaskCompletionSource<Thread> copyThreadCompletionSource = new TaskCompletionSource<Thread>(); 

// This will await while any of both given tasks end. 
await Task.WhenAny 
(
    timeoutTask, 
    Task.Factory.StartNew 
    (
     () => 
     { 
      // This will let main thread access this thread and force a Thread.Abort 
      // if the operation must be canceled due to a timeout 
      copyThreadCompletionSource.SetResult(Thread.CurrentThread); 
      File.Copy(@"C:\x.txt", @"C:\y.txt"); 
     } 
    ) 
); 


// Since timeoutTask was completed before wrapped File.Copy task you can 
// consider that the operation timed out 
if (timeoutTask.Status == TaskStatus.RanToCompletion) 
{ 
    // Timed out! 
    Thread copyThread = await copyThreadCompletionSource.Task; 
    copyThread.Abort(); 
} 

你可能封装这一重新使用它,只要你想:

public static class Timeout 
{ 
    public static async Task<bool> ForAsync(Action operationWithTimeout, TimeSpan maxTime) 
    { 
     Contract.Requires(operationWithTimeout != null); 

     Task timeoutTask = Task.Delay(maxTime); 
     TaskCompletionSource<Thread> copyThreadCompletionSource = new TaskCompletionSource<Thread>(); 

     // This will await while any of both given tasks end. 
     await Task.WhenAny 
     (
      timeoutTask, 
      Task.Factory.StartNew 
      (
       () => 
       { 
        // This will let main thread access this thread and force a Thread.Abort 
        // if the operation must be canceled due to a timeout 
        copyThreadCompletionSource.SetResult(Thread.CurrentThread); 
        operationWithTimeout(); 
       } 
      ) 
     ); 


     // Since timeoutTask was completed before wrapped File.Copy task you can 
     // consider that the operation timed out 
     if (timeoutTask.Status == TaskStatus.RanToCompletion) 
     { 
      // Timed out! 
      Thread copyThread = await copyThreadCompletionSource.Task; 
      copyThread.Abort(); 

      return false; 
     } 
     else 
     { 
      return true; 
     }    
    } 
} 

某处项目你可以这样称呼上述方法:

bool success = await Timeout.ForAsync(() => File.Copy(...), TimeSpan.FromSeconds(10)); 

if(success) 
{ 
    // Do stuff if File.Copy didn't time out! 
} 

注意我已经使用Thread.Abort()而不是使用CancellationToken。在你的用例中,你需要调用一个你不能使用所谓的取消模式的同步方法,我相信这可能是Thread.Abort()可能是有效选项的少数情况之一。

在一天结束时,如果出现超时,代码将中止执行File.Copy的线程,因此,它应该足以停止I/O操作。

+0

感谢您的回答。现在我有一个后续问题:如果超时发生在File.Copy完成之前,我可以取消File.Copy任务,如果是这样,如何? – JeffR

+0

@JeffR我会在今天或明天回答您的问题...顺便说一句,您可以通过查看以下文章来加速答案,并且您应该能够修改我的示例以满足您的需求:https://msdn.microsoft .com/en-us/library/dd997396%28v = vs.110%29.aspx –

+0

谢谢。我看到了,但我不明白如何中断File.Copy - File.Copy在请求时如何抛出异常? – JeffR

0

你可以实现一个简单的方法类似于下面的东西,建立在Stream.CopyToAsync()其接受取消标记:

static async Task Copy(string destFilePath, string sourceFilePath, int timeoutSecs) 
{ 
    var cancellationSource = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSecs)); 

    using (var dest = File.Create(destFilePath)) 
    using (var src = File.OpenRead(sourceFilePath)) 
    { 
     await src.CopyToAsync(dest, 81920, cancellationSource.Token); 
    } 
} 

正如你所看到的,它可以创建一个CancellationTokenSource()其指定后自动取消本身时间。

try 
{ 
    await Copy(@"c:\temp\test2.bin", @"c:\temp\test.bin", 60); 
    Console.WriteLine("finished.."); 
} 
catch (OperationCanceledException ex) 
{ 
    Console.WriteLine("cancelled.."); 
} 
catch (Exception ex) 
{ 
    Console.WriteLine("error.."); 
} 

或旧的方式:

var copyInProgress = Copy(@"c:\temp\test2.bin", @"c:\temp\test.bin", 60); 

copyInProgress.ContinueWith(
     _ => { Console.WriteLine("cancelled.."); }, 
     TaskContinuationOptions.OnlyOnCanceled 
    ); 

copyInProgress.ContinueWith(
     _ => { Console.WriteLine("finished.."); }, 
     TaskContinuationOptions.OnlyOnRanToCompletion 
    ); 

copyInProgress.ContinueWith(
     _ => { Console.WriteLine("failed.."); }, 
     TaskContinuationOptions.OnlyOnFaulted 
    ); 

copyInProgress.Wait(); 

这是很容易改进上述代码,以使用一个第二消除令牌可被控制

可以使用异步使用复制方法由用户(通过取消按钮)。所有你需要使用的是CancellationTokenSource.CreateLinkedTokenSource