gpt4 book ai didi

asynchronous - Async.StartChild 是否有内存泄漏?

转载 作者:行者123 更新时间:2023-12-03 12:55:27 26 4
gpt4 key购买 nike

当我运行以下测试(使用 F#2.0 构建)时,我得到了 OutOfMemoryException。在我的系统上出现异常大约需要 5 分钟(如果它作为 x86 进程运行,则 i7-920 6gb ram),但无论如何我们可以看到任务管理器中的内存是如何增长的。

module start_child_test
open System
open System.Diagnostics
open System.Threading
open System.Threading.Tasks

let cnt = ref 0
let sw = Stopwatch.StartNew()
Async.RunSynchronously(async{
while true do
let! x = Async.StartChild(async{
if (Interlocked.Increment(cnt) % 100000) = 0 then
if sw.ElapsedMilliseconds > 0L then
printfn "ops per sec = %d" (100000L*1000L / sw.ElapsedMilliseconds)
else
printfn "ops per sec = INF"
sw.Restart()
GC.Collect()
})
do! x
})

printfn "done...."

我看不出这段代码有什么问题,也看不出内存增长的任何原因。我进行了替代实现以确保我的论点有效:
module start_child_fix
open System
open System.Collections
open System.Collections.Generic
open System.Threading
open System.Threading.Tasks


type IAsyncCallbacks<'T> = interface
abstract member OnSuccess: result:'T -> unit
abstract member OnError: error:Exception -> unit
abstract member OnCancel: error:OperationCanceledException -> unit
end

type internal AsyncResult<'T> =
| Succeeded of 'T
| Failed of Exception
| Canceled of OperationCanceledException

type internal AsyncGate<'T> =
| Completed of AsyncResult<'T>
| Subscribed of IAsyncCallbacks<'T>
| Started
| Notified

type Async with
static member StartChildEx (comp:Async<'TRes>) = async{
let! ct = Async.CancellationToken

let gate = ref AsyncGate.Started
let CompleteWith(result:AsyncResult<'T>, callbacks:IAsyncCallbacks<'T>) =
if Interlocked.Exchange(gate, Notified) <> Notified then
match result with
| Succeeded v -> callbacks.OnSuccess(v)
| Failed e -> callbacks.OnError(e)
| Canceled e -> callbacks.OnCancel(e)

let ProcessResults (result:AsyncResult<'TRes>) =
let t = Interlocked.CompareExchange<AsyncGate<'TRes>>(gate, AsyncGate.Completed(result), AsyncGate.Started)
match t with
| Subscribed callbacks ->
CompleteWith(result, callbacks)
| _ -> ()
let Subscribe (success, error, cancel) =
let callbacks = {
new IAsyncCallbacks<'TRes> with
member this.OnSuccess v = success v
member this.OnError e = error e
member this.OnCancel e = cancel e
}
let t = Interlocked.CompareExchange<AsyncGate<'TRes>>(gate, AsyncGate.Subscribed(callbacks), AsyncGate.Started)
match t with
| AsyncGate.Completed result ->
CompleteWith(result, callbacks)
| _ -> ()

Async.StartWithContinuations(
computation = comp,
continuation = (fun v -> ProcessResults(AsyncResult.Succeeded(v))),
exceptionContinuation = (fun e -> ProcessResults(AsyncResult.Failed(e))),
cancellationContinuation = (fun e -> ProcessResults(AsyncResult.Canceled(e))),
cancellationToken = ct
)
return Async.FromContinuations( fun (success, error, cancel) ->
Subscribe(success, error, cancel)
)
}

对于这个测试,它运行良好,没有任何显着的内存消耗。不幸的是,我在 F# 方面没有太多经验,并且怀疑我是否错过了一些东西。如果是错误,我该如何向 F# 团队报告?

最佳答案

我认为您是正确的 - StartChild 的实现似乎存在内存泄漏.
我做了一些分析(在 fantastic tutorial by Dave Thomas 之后)和 open-source F# release我想我什至知道如何解决这个问题。如果你看一下 StartChild 的实现,它使用工作流的当前取消 token 注册一个处理程序:

let _reg = ct.Register(
(fun _ ->
match !ctsRef with
| null -> ()
| otherwise -> otherwise.Cancel()), null)
在堆中保持事件的对象是这个注册函数的实例。他们可以通过调用 _reg.Dispose() 取消注册。 ,但在 F# 源代码中永远不会发生这种情况。我尝试添加 _reg.Dispose()到异步完成时调用的函数:
(fun res -> _reg.Dispose(); ctsRef := null; resultCell.RegisterResult (Ok res, reuseThread=true))   
(fun err -> _reg.Dispose(); ctsRef := null; resultCell.RegisterResult (Error err,reuseThread=true))
(fun err -> _reg.Dispose(); ctsRef := null; resultCell.RegisterResult (Canceled err,reuseThread=true))
...根据我的实验,这解决了问题。因此,如果您想要解决方法,您可以从 control.fs 复制所有必需的代码。并将其添加为修复程序。
我将向 F# 团队发送错误报告,其中包含指向您问题的链接。如果您发现其他问题,您可以通过发送错误报告至 fsbugs 与他们联系。在 microsoftcom .

关于asynchronous - Async.StartChild 是否有内存泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9045534/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com