要异步复制文件,这样的工作会起作用吗?
let filecopyasync (source, target) =
let task = Task.Run((fun () ->File.Copy(source, target, true)))
// do other stuff
Async.AwaitIAsyncResult task
特别是,当我做其他事情"这会激发一个新线程来复制副本吗?
更新:
找到另一种解决方案:
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
答案 0 :(得分:3)
您的问题是将两个问题混为一谈,即多线程和异步。重要的是要意识到这些事情是完全不同的概念:
Asychrony是关于任务的工作流程,我们独立于主程序流响应这些任务的完成。
多线程是一种执行模型,可以用来实现异步,尽管可以通过其他方式实现异步(例如硬件中断)。
现在,当涉及到I / O时,你应该不提出的问题是“我可以启动另一个线程为我做这件事吗?”
为什么,你问?
如果在主线程中执行了一些I / O操作,通常会阻塞主线程等待结果。如果你通过创建一个新线程来逃避这个问题,你实际上并没有解决这个问题,你只是把它移动了。现在您已经阻止了您创建的新线程或线程池线程。亲爱的,同样的问题。
线程是一种昂贵且有价值的资源,在等待阻止I / O完成时不应浪费。
那么,真正的解决方案是什么?
好吧,我们通过其中一种方法实现异步。这样,我们可以请求操作系统执行一些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.Copy
是一个返回unit
的同步方法,而CopyToAsync
是一个返回{的异步方法{1}}。你不能通过在它们周围放置异步包装来神奇地使同步方法同步,而是需要确保你一直使用异步。
答案 1 :(得分:1)
您可以使用一些printfns自行测试。我发现我必须RunAsynchronously强制主线程等待副本完成。我不确定为什么await不起作用,但你可以看到预期的输出集合,表明副本发生在后台。
{
"Campaign1": {
"reports": [
{
"month": "Google - January - Monthly Report.csv",
"impressions": 53,
"clicks": 31,
"cost": 18
},
{
"month": "Google - February - Monthly Report.csv",
"impressions": 0,
"clicks": 0,
"cost": 0
},
{
"month": "Google - March - Monthly Report.csv",
"impressions": 0,
"clicks": 0,
"cost": 0
},
{
"month": "Google - April - Monthly Report.csv",
"impressions": 13,
"clicks": 11,
"cost": 8
}
]
},
"Campaign2": {
"reports": [
{
"month": "Google - January - Monthly Report.csv",
"impressions": 0,
"clicks": 0,
"cost": 0
},
{
"month": "Google - February - Monthly Report.csv",
"impressions": 0,
"clicks": 0,
"cost": 0
},
{
"month": "Google - March - Monthly Report.csv",
"impressions": 13,
"clicks": 11,
"cost": 8
},
{
"month": "Google - April - Monthly Report.csv",
"impressions": 0,
"clicks": 0,
"cost": 0
}
]
}
}
输出:
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"
答案 2 :(得分:1)
如果您尝试做的只是在其他线程上运行某些内容,那么您的初始Task.Run
方法应该没问题(请注意,如果您打电话,可以获得Task<unit>
Task.Run<_>
代替非通用Task.Run
,可能稍微容易处理。
但是,您应该清楚自己的目标 - 可以说“正确的”异步文件副本不需要单独的.NET线程(这是一个相对较重的原语)并且将依赖于完成端口等操作系统功能代替;由于System.IO.File
未提供原生CopyAsync
方法,因此您需要编写自己的方法(请参阅https://stackoverflow.com/a/35467471/82959了解一个易于音译的简单C#实现)。