使用 File.Copy 时如何实现超时?
How do I implement a timeout when using File.Copy?
我正在使用 System.IO.File.Copy 将文件从远程共享复制到我的本地系统。如果复制时间过长,如何实现超时?
例如,可以使用 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 操作。
您可以实现类似以下的简单方法,该方法建立在接受取消令牌的 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
我正在使用 System.IO.File.Copy 将文件从远程共享复制到我的本地系统。如果复制时间过长,如何实现超时?
例如,可以使用 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 操作。
您可以实现类似以下的简单方法,该方法建立在接受取消令牌的 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