C #에서 비동기 파일 복사 / 이동
C #에서 비동기 적으로 파일 복사 / 이동을 수행하는 올바른 방법은 무엇입니까?
비동기 프로그래밍의 개념은 비동기 IO가 완료되는 동안 호출 스레드 (스레드 풀 스레드라고 가정)가 다른 작업에 사용하기 위해 스레드 풀로 돌아가도록 허용하는 것입니다. 내부적으로 호출 컨텍스트는 데이터 구조에 채워지고 하나 이상의 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를 이해하는 것이 도움이 될 것입니다. 라인에 대해 완전히 같은 라인이 아닐 수도 있지만 아이디어는 정말 가깝습니다. "비동기"메서드에서 호출 스택을 보면 MoveNext가 표시됩니다.
이동이 진행되는 한 실제로 "이동"이고 복사본이 아닌 경우 비동기 일 필요가 없습니다. 이동은 파일 테이블에 대한 빠른 원자 적 작업입니다. 파일을 다른 파티션으로 이동하지 않는 경우에만 그렇게 작동합니다.
다음은 우리가 순차적으로 읽고 쓰고 있다는 OS 힌트를 제공하는 비동기 파일 복사 방법입니다. 따라서 읽기시 데이터를 미리 가져오고 쓰기 준비를 할 수 있습니다.
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로 코드를 약간 개선했습니다 (성능 및 취소).
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);
}
비동기 대리자를 사용할 수 있습니다.
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 코딩의 다양한 기술을 알려줍니다.
당신이 피하고 싶은 것 어떤 상황이 있지만 Task.Run
, Task.Run(() => File.Move(source, dest)
작동합니다. 파일이 동일한 디스크 / 볼륨에서 단순히 이동되는 경우 헤더는 변경되지만 파일 내용은 이동되지 않으므로 거의 즉각적인 작업이므로 고려할 가치가 있습니다. 다양한 "순수한"비동기 메서드는이를 수행 할 필요가없는 경우에도 항상 스트림을 복사하므로 실제로는 속도가 상당히 느려질 수 있습니다.
이 기사에서 제안한 대로 수행 할 수 있습니다 .
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);
AFAIK, there is no high level async API to copy a file. However, you can build your own API to accomplish that task using Stream.BeginRead/EndRead
and Stream.BeginWrite/EndWrite
APIs. Alternatively, you can use BeginInvoke/EndInvoke
method as mentioned in the answers here, but you have to keep in mind, that they won't be non blocking async I/O. They merely perform the task on a separate thread.
The correct way to copy: use a separate thread.
Here's how you might be doing it (synchronously):
//.. [code]
doFileCopy();
// .. [more code]
Here's how to do it asynchronously:
// .. [code]
new System.Threading.Thread(doFileCopy).Start();
// .. [more code]
This is a very naive way to do things. Done well, the solution would include some event/delegate method to report the status of the file copy, and notify important events like failure, completion etc.
cheers, jrh
I would suggest that the File Copy IO function, available in the .Net programming languages, is asynchronous in any case. After using it within my program to move small files, it appears that subsequent instructions begin to execute before the actual file copy is finished. I'm gussing that the executable gives Windows the task to do the copy and then immediately returns to execute the next instruction - not waiting for Windows to finish. This forces me to construct while loops just after the call to copy that will execute until I can confirm the copy is complete.
참고URL : https://stackoverflow.com/questions/882686/asynchronous-file-copy-move-in-c-sharp
'Program Tip' 카테고리의 다른 글
System.nanoTime ()이 System.currentTimeMillis ()보다 성능이 훨씬 느린 이유는 무엇입니까? (0) | 2020.12.01 |
---|---|
명령 결과를 bash에서 인수로 사용합니까? (0) | 2020.12.01 |
CouchDB에서 일반 사용자 만들기 (0) | 2020.12.01 |
프로토 타입으로 이벤트 트리거 (0) | 2020.11.30 |
django 템플릿 시스템, 모델 내부에서 함수 호출 (0) | 2020.11.30 |