F#异步文件复制

时间:2017-04-04 13:56:32

标签: asynchronous f#

要异步复制文件,这样的工作会起作用吗?

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

3 个答案:

答案 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#实现)。