为什么我不能在 F# 中的异步块中使用 'use'
Why can't I use 'use' in an async block, in F#
我有这个函数可以从缓存中读取数据:
let private tryLoadFromCacheAsync filename =
async {
let filespec = makePath filename
match File.Exists filespec with
| true ->
let! bytes = File.ReadAllBytesAsync(filespec) |> Async.AwaitTask
use pBytes = fixed bytes
let sourceSpan = Span<byte>(NativePtr.toVoidPtr pBytes, bytes.Length)
return Some (MemoryMarshal.Cast<byte, ShortTradeData>(sourceSpan).ToArray())
| false ->
return None
}
兴趣线在这里:
use pBytes = fixed bytes
编译失败:
The type 'nativeptr<'a>' is not compatible with the type 'IDisposable'
但该行将编译为:
let pBytes = fixed bytes
这是在 async 块中发生的。
这是为什么?
问题与 use
部分无关,而与 fixed
部分有关。 fixed 关键字确保值保存在堆栈中。将它与 async
结合使用的问题是计算表达式中使用的局部变量并不总是保存在堆栈中。如果在它们的使用之间有 let!
,它们需要存储为对象的字段,因此 fixed
不能在这种情况下工作。
您可以通过将固定变量的范围限制为 non-async 代码块来解决此问题:
let private tryLoadFromCacheAsync filename =
async {
let filespec = makePath filename
match File.Exists filespec with
| true ->
let! bytes = File.ReadAllBytesAsync(filespec) |> Async.AwaitTask
let res =
use pBytes = fixed bytes
let sourceSpan = Span<byte>(NativePtr.toVoidPtr pBytes, bytes.Length)
MemoryMarshal.Cast<byte, ShortTradeData>(sourceSpan).ToArray()
return Some res
| false ->
return None
}
我有这个函数可以从缓存中读取数据:
let private tryLoadFromCacheAsync filename =
async {
let filespec = makePath filename
match File.Exists filespec with
| true ->
let! bytes = File.ReadAllBytesAsync(filespec) |> Async.AwaitTask
use pBytes = fixed bytes
let sourceSpan = Span<byte>(NativePtr.toVoidPtr pBytes, bytes.Length)
return Some (MemoryMarshal.Cast<byte, ShortTradeData>(sourceSpan).ToArray())
| false ->
return None
}
兴趣线在这里:
use pBytes = fixed bytes
编译失败:
The type 'nativeptr<'a>' is not compatible with the type 'IDisposable'
但该行将编译为:
let pBytes = fixed bytes
这是在 async 块中发生的。
这是为什么?
问题与 use
部分无关,而与 fixed
部分有关。 fixed 关键字确保值保存在堆栈中。将它与 async
结合使用的问题是计算表达式中使用的局部变量并不总是保存在堆栈中。如果在它们的使用之间有 let!
,它们需要存储为对象的字段,因此 fixed
不能在这种情况下工作。
您可以通过将固定变量的范围限制为 non-async 代码块来解决此问题:
let private tryLoadFromCacheAsync filename =
async {
let filespec = makePath filename
match File.Exists filespec with
| true ->
let! bytes = File.ReadAllBytesAsync(filespec) |> Async.AwaitTask
let res =
use pBytes = fixed bytes
let sourceSpan = Span<byte>(NativePtr.toVoidPtr pBytes, bytes.Length)
MemoryMarshal.Cast<byte, ShortTradeData>(sourceSpan).ToArray()
return Some res
| false ->
return None
}