Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asynchronous F#异步文件拷贝_Asynchronous_F# - Fatal编程技术网

Asynchronous F#异步文件拷贝

Asynchronous F#异步文件拷贝,asynchronous,f#,Asynchronous,F#,要异步复制一个文件,像这样的东西可以工作吗 let filecopyasync (source, target) = let task = Task.Run((fun () ->File.Copy(source, target, true))) // do other stuff Async.AwaitIAsyncResult task 特别是,当我“做其他事情”时,会不会触发一个新的线程来进行复制 更新: 找到了另一个解决方案: let asyncFileC

要异步复制一个文件,像这样的东西可以工作吗

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
让asyncFileCopy(源、目标、覆盖)=
printfn“将%s复制到%s”源目标
设fn=newfunc(File.Copy)
Async.FromBeginEnd((源、目标、覆盖),fn.BeginInvoke,fn.EndInvoke)
让copyfile1=asyncFileCopy(“file1”、“file2”,true)
让copyfile2=asyncFileCopy(“file3”,“file4”,true)
[copyfile1;copyfile2]|>seq |>Async.Parallel |>Async.RunSynchronously |>忽略

您可以自己用一些printfn进行测试。我发现我必须异步运行以强制主线程等待副本完成。我不知道为什么等待不起作用,但您可以看到预期的输出集,指示复制发生在后台

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

如果您所要做的只是在另一个线程上运行某个任务,而执行其他任务,那么您最初的
任务.run
方法应该很好(请注意,如果调用
任务.run
,您可以得到
任务
,而不是非通用的
任务.run
,这可能更容易处理)


但是,您应该清楚自己的目标——可以说,“适当的”异步文件拷贝不需要单独的.NET线程(这是一个相对较重的原语),而是依赖于操作系统功能,如完成端口;由于
System.IO.File
没有提供本机
copySync
方法,您需要编写自己的方法(请参阅一个简单的C#实现,该实现很容易音译)。

您的问题将两个问题混为一谈,即多线程和异步。重要的是要认识到这些东西是完全不同的概念:

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
然后

[]
设默认缓冲区大小为4096
让copyToAsync源目标=
异步的{
使用sourceFile=newfilestream(source,FileMode.Open,FileAccess.Read,FileShare.Read,默认缓冲区大小,true);
使用destFile=newfilestream(dest,FileMode.OpenOrCreate,FileAccess.Write,FileShare.None,默认缓冲区大小,true);
do!sourceFile.CopyToAsync(destFile)|>Async.task
}
然后,您可以将其与
Async.Parallel
一起使用,以同时执行多个拷贝


注意:这与您上面写的不同,因为
File.Copy
是一种同步方法,返回
unit
,而
CopyToAsync
是一种异步方法,返回
Task
。通过在同步方法周围放置异步包装器,您无法神奇地使同步方法异步,相反,您需要确保一直在使用异步。

这看起来不错!下一个问题:我能确定线程池在任何给定时间有多大吗?我想限制我的线程使用,以避免淹没系统。e、 如果我复制了很多文件,其中一些文件相当大。假设我只想使用5个线程(任意)。NET任务使用线程池-CLR将管理您的任务并在线程池中安排它们。如果要限制创建的任务数量,此C#答案可能会有所帮助:如果创建的流没有
FileOptions.Asynchronous
标志,那么
CopyToAsync
是否会实际执行异步复制?参见,例如…@kvb不确定。很多例子都表明这是可以的,但快速查看参考源会让我更加怀疑。我把它改成安全的。所以这看起来很酷!也非常感谢你的解释。您将如何增强它以在IOException上执行给定次数的重试?@user1443098类似于此答案中的
RetryRun
函数:,然后您可以例如
RetryRun 5(copytosync source dest)
@user1443098,也许是为了更实际和更健壮的使用,您需要考虑什么可能导致此类异常,以及如何通过重试以外的方式从异常中恢复。
[<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
    }