2017-04-04 9 views
1

ファイルを非同期にコピーするには、次のようなものがありますか?F#非同期ファイルコピー

let filecopyasync (source, target) = 
    let task = Task.Run((fun() ->File.Copy(source, target, true))) 

    // do other stuff 

    Async.AwaitIAsyncResult task 

特に、これは私が「他のものをやっている」ときにコピーを行う新しいスレッドを起動しますか?

UPDATE:

let asyncFileCopy (source, target, overwrite) = 
    printfn "Copying %s to %s" source target 
    let fn = new Func<string * string * bool, unit>(File.Copy) 
    Async.FromBeginEnd((source, target, overwrite), fn.BeginInvoke, fn.EndInvoke) 

let copyfile1 = asyncFileCopy("file1", "file2", true) 
let copyfile2 = asyncFileCopy("file3", "file4", true) 

[copyfile1; copyfile2] |> seq |> Async.Parallel |> Async.RunSynchronously |> ignore 

答えて

1

あなたは、いくつかのprintfnsでそれを自分でテストすることができます。

は別の解決策を見つけました。主なスレッドがコピーが完了するのを待つようにRunAsynchronouslyを強制しなければならないことがわかりました。私はなぜ待たれていないのか分かりませんが、コピーがバックグラウンドで起こったことを示す予想される出力を見ることができます。

open System 
open System.IO 
open System.Threading 
open System.Threading.Tasks 
let filecopyasync (source, target) = 
    let task = Task.Run((fun() -> 
      printfn "CopyThread: %d" Thread.CurrentThread.ManagedThreadId; 
      Thread.Sleep(10000); 
      File.Copy(source, target, true); printfn "copydone")) 

    printfn "mainThread: %d" Thread.CurrentThread.ManagedThreadId; 
    let result=Async.AwaitIAsyncResult task 
    Thread.Sleep(3000) 
    printfn "doing stuff" 
    Async.RunSynchronously result 
    printfn "done" 

出力:あなたが何かを行う一方で、あなたがやろうとしているすべては、別のスレッドに何かを実行している場合

filecopyasync (@"foo.txt",@"bar.txt");; 
mainThread: 1 
CopyThread: 7 
doing stuff 
copydone 
done 
+0

これはよさそうですね!次の質問:ある時点でスレッドプールの大きさを判断できますか?私はシステムを圧倒することを避けるためにスレッドの使用を制限したいと思います。例えば私が多くのファイルをコピーしていて、かなり大きいファイルがある場合。 5スレッド(任意)を使用したかったとします。何とかそれを見つけることができますか? – user1443098

+0

.NETタスクはThreadPoolを使用します.CLRはタスクを管理し、プール上でスケジュールします。あなたが作成するタスクの数を制限したい場合、このC#の答えは以下のように役立ちます:http://stackoverflow.com/questions/2898609/system-threading-tasks-limit-the-number-of-concurrent-tasks –

1

、その後、あなたの最初のTask.Runアプローチは問題ないはずです(あなたが得ることができることに注意してくださいTask.Runの代わりにTask.Run<_>に電話するとTask<unit>と表示されます。

しかし、 "適切な"非同期ファイルコピーでは、比較的重いプリミティブである別の.NETスレッドを必要とせず、完了のようなオペレーティングシステムの機能に依存しているはずです代わりにポート。 System.IO.FileはネイティブのCopyAsyncメソッドを提供していないので、独自に記述する必要があります(簡単なC#の実装では、簡単に言い訳ができます)https://stackoverflow.com/a/35467471/82959を参照してください。

1

あなたの質問は、マルチスレッドとアシクロンという2つの問題が融合しています。これらは全く異なる概念であることに気付くことが重要です。

Asychronyは、メインプログラムフローとは独立してこれらのタスクの完了に対応するタスクのワークフローです。

マルチスレッドは実行モデルであり、アスキーロニーを実装するために使用できますが、ハードウェア割り込みなど他の方法でアシクロンを実行することもできます。今


、それはされますがが尋ねるべきでないI/O、質問に来るとき「私は私のためにそれを行うには、別のスレッドをスピンアップすることはできますか?」

なぜですか?

メインスレッドでI/Oを行う場合、通常はメインスレッドが結果を待つのをブロックします。新しいスレッドを作成してこの問題を回避した場合、実際に問題を解決したことはなく、今度はそれを移動しただけです。これで、作成した新しいスレッドまたはスレッドプールスレッドがブロックされました。ああああ、同じ問題。

スレッドは高価で貴重なリソースであり、I/Oのブロックが完了するのを待つ際に浪費されるべきではありません。

実際の解決策は何ですか?

さて、私たちは、これらの他のアプローチの1つを使って非同期性を達成しています。このようにして、OSがいくつかのI/Oを実行するように要求し、I/O操作が完了したときにそれを知らせるように要求することができます。これにより、結果を待っている間にスレッドがブロックされることはありません。 Windowsでは、これはI/O完了ポートと呼ばれるものを介して実装されています。


これをF#でどうやって行うのですか?

.NET CopyToAsyncメソッドがおそらく最も簡単な方法です。これはプレーンなタスクを返すので、それはヘルパーメソッドを作成すると便利です:

type Async with 
    static member AwaitPlainTask (task : Task) = 
     task.ContinueWith(ignore) |> Async.AwaitTask 

その後

[<Literal>] 
let DEFAULT_BUFFER_SIZE = 4096 

let copyToAsync source dest = 
    async { 
     use sourceFile = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.Read, DEFAULT_BUFFER_SIZE, true); 
     use destFile = new FileStream(dest, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, DEFAULT_BUFFER_SIZE, true); 
     do! sourceFile.CopyToAsync(destFile) |> Async.AwaitPlainTask 
    } 

あなたはその後、同時に複数のコピーを実行するためにAsync.Parallelでこれを使用することができます。

注:これはFile.CopyCopyToAsyncTaskを返す非同期メソッドである一方、unitを返すsychronous方法ですので、あなたが上記の書いたものとは異なっています。非同期ラッパーをそれらのまわりに置くことによって、魔法のように同期メソッドを非同期にすることはできません。代わりに非同期メソッドを完全に使用していることを確認する必要があります。

+0

' CopyToAsync'はストリームが 'FileOptions.Asynchronous'フラグなしで作成された場合、実際に非同期コピーを実行しますか?例えば、 http://stackoverflow.com/a/35467471/82959 – kvb

+0

@kvbわかりません。多くの例から、これは大丈夫だと示唆されていますが、参照元をすばやく見れば疑問が増えます。私はそれを安全な側に変更しました。 – TheInnerLight

+0

だからこれはクールに見えます!説明もありがとうございました。どのようにしてIOExceptionの再試行回数を増やすことができますか? – user1443098