C# C语言中的非阻塞文件复制#
如何在不阻塞线程的情况下用C#复制文件?正确的复制方法:使用单独的线程 以下是您可能(同步)执行的方式: 以下是如何异步执行此操作:C# C语言中的非阻塞文件复制#,c#,asynchronous,C#,Asynchronous,如何在不阻塞线程的情况下用C#复制文件?正确的复制方法:使用单独的线程 以下是您可能(同步)执行的方式: 以下是如何异步执行此操作: //.. [code] doFileCopy(); // .. [more code] // .. [code] new System.Threading.Thread(doFileCopy).Start(); // .. [more code] 这是一种非常幼稚的做事方式。如果做得好,解决方案将包括一些事件/委托方法来报告文件副本的状态,并通知重要事件,如失
//.. [code]
doFileCopy();
// .. [more code]
// .. [code]
new System.Threading.Thread(doFileCopy).Start();
// .. [more code]
这是一种非常幼稚的做事方式。如果做得好,解决方案将包括一些事件/委托方法来报告文件副本的状态,并通知重要事件,如失败、完成等
干杯,
jrh您可以使用异步委托
public class AsyncFileCopier
{
public delegate void FileCopyDelegate(string sourceFile, string destFile);
public static void AsynFileCopy(string sourceFile, string destFile)
{
FileCopyDelegate del = new FileCopyDelegate(FileCopy);
IAsyncResult result = del.BeginInvoke(sourceFile, destFile, CallBackAfterFileCopied, null);
}
public static void FileCopy(string sourceFile, string destFile)
{
// Code to copy the file
}
public static void CallBackAfterFileCopied(IAsyncResult result)
{
// Code to be run after file copy is done
}
}
您可以将其称为:
AsyncFileCopier.AsynFileCopy("abc.txt", "xyz.txt");
这告诉您asyn编码的不同技术您可以按照文章的建议进行:
public static void CopyStreamToStream(
Stream source, Stream destination,
Action<Stream, Stream, Exception> completed)
{
byte[] buffer = new byte[0x1000];
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(null);
Action<Exception> done = e =>
{
if(completed != null) asyncOp.Post(delegate
{
completed(source, destination, e);
}, null);
};
AsyncCallback rc = null;
rc = readResult =>
{
try
{
int read = source.EndRead(readResult);
if(read > 0)
{
destination.BeginWrite(buffer, 0, read, writeResult =>
{
try
{
destination.EndWrite(writeResult);
source.BeginRead(
buffer, 0, buffer.Length, rc, null);
}
catch(Exception exc) { done(exc); }
}, null);
}
else done(null);
}
catch(Exception exc) { done(exc); }
};
source.BeginRead(buffer, 0, buffer.Length, rc, null);
公共静态无效CopyStreamToStream(
流源、流目的地、,
行动完成)
{
字节[]缓冲区=新字节[0x1000];
AsyncOperation asyncOp=AsyncOperationManager.CreateOperation(空);
动作完成=e=>
{
如果(已完成!=null)asyncOp.Post(委托
{
已完成(来源、目的地、e);
},空);
};
异步回调rc=null;
rc=readResult=>
{
尝试
{
int read=source.EndRead(readResult);
如果(读取>0)
{
destination.BeginWrite(缓冲区,0,读,写结果=>
{
尝试
{
destination.EndWrite(writeResult);
来源:beginhead(
buffer,0,buffer.Length,rc,null);
}
捕获(异常exc){done(exc);}
},空);
}
否则完成(空);
}
捕获(异常exc){done(exc);}
};
BeginRead(buffer,0,buffer.Length,rc,null);
AFAIK,没有高级异步API来复制文件。但是,您可以使用Stream.BeginRead/EndRead
和Stream.BeginWrite/EndWrite
API构建自己的API来完成该任务。或者,您可以使用BeginInvoke/EndInvoke
方法,如这里的答案所述,但您必须保持在mi中nd,它们不会是非阻塞异步I/O。它们只在单独的线程上执行任务。我建议.Net编程语言中提供的File Copy IO函数在任何情况下都是异步的。在我的程序中使用它移动小文件后,后续指令似乎在实际文件复制完成。我认为可执行文件给Windows任务进行复制,然后立即返回执行下一条指令,而不是等待Windows完成。这迫使我在调用复制后立即构造while循环,直到我确认复制完成为止。异步的思想编程就是允许调用线程(假设它是线程池线程)在异步IO完成时返回线程池以用于其他任务。在后台,调用上下文将填充到数据结构中,1个或多个IO完成线程将监视等待完成的调用。当IO完成时,完成线程将调用回线程池以还原调用上下文。这样,而不是100个线程阻塞只有完成线程和几个线程池线程处于空闲状态
我能想到的最好办法是:
public async Task CopyFileAsync(string sourcePath, string destinationPath)
{
using (Stream source = File.Open(sourcePath))
{
using(Stream destination = File.Create(destinationPath))
{
await source.CopyToAsync(destination);
}
}
}
不过我还没有对此做过广泛的性能测试。我有点担心,因为如果它这么简单的话,它已经在核心库中了
await做了我在幕后描述的事情。如果你想大致了解它的工作原理,了解Jeff Richter的AsyncEnumerator可能会有所帮助。它们可能不完全相同,但想法非常接近。如果你从“async”方法中查看调用堆栈,你会看到MoveNext
就移动而言,如果它真的是“移动”,则不需要异步而不是复制然后删除。移动是针对文件表的快速原子操作。只有当您不尝试将文件移动到其他分区时,它才起作用。这里有一个异步文件复制方法,它向操作系统提示我们按顺序读写,以便它可以在读取时预取数据并准备就绪至于写作:
public static async Task CopyFileAsync(string sourceFile, string destinationFile)
{
using (var sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.Asynchronous | FileOptions.SequentialScan))
using (var destinationStream = new FileStream(destinationFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, 4096, FileOptions.Asynchronous | FileOptions.SequentialScan))
await sourceStream.CopyToAsync(destinationStream);
}
您也可以尝试使用缓冲区大小。这里是4096字节。我通过@DrewNoakes略微增强了代码(性能和取消):
在某些情况下,您可能希望避免
Task.Run
,Task.Run(()=>File.Move(source,dest)
将起作用。这是值得考虑的,因为当一个文件在同一磁盘/卷中移动时,这几乎是一个瞬间的操作,因为头会更改,但文件内容不会移动。各种“纯”异步方法总是复制流,即使不需要这样做,因此在实践中可能会慢一些。我认为问题是异步执行操作,而不消耗线程。有多种方法可以将工作委托给线程池,其中大多数比此处的机制更简单。请你告诉我,它是什么意思(wait source.CopyToAsync(destination);)?在标记为aync的方法中,wait在内部所做的是等待等待等待的代码片段完成。天真地说,我们可以说它阻塞了。但是它并没有真正阻塞。真正的阻塞行为就像wait()使活动线程在执行点停留。Await实际上会使线程正在执行的任何操作的上下文停留在数据结构中,并允许活动线程返回到线程池,在该线程池中它可以用于其他事情。当Await返回线程池线程时(可能不是同一个线程)检索内容
public static async Task CopyFileAsync(string sourceFile, string destinationFile, CancellationToken cancellationToken)
{
var fileOptions = FileOptions.Asynchronous | FileOptions.SequentialScan;
var bufferSize = 4096;
using (var sourceStream =
new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, fileOptions))
using (var destinationStream =
new FileStream(destinationFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, bufferSize, fileOptions))
await sourceStream.CopyToAsync(destinationStream, bufferSize, cancellationToken)
.ConfigureAwait(continueOnCapturedContext: false);
}