是否可以在我的计算表达式中提供setter函数?



我正在尝试编写一个f#计算表达式,它只允许从临界区内读写线程安全变量。

我有一个类型ThreadSafeVar<'t>,它包装了一个值,一个CriticalSection<'t>和一个计算表达式构建器LockContext,如下所示:


// wraps a value and restricts access to it
type ThreadSafeVar<'t> (value: 't) =
member val internal Value = value with get, set
// Encapsulates a critical section
type CriticalSection<'t> =
private
{ LockObj: obj
fn: unit -> 't }

static member Lock(lc: CriticalSection<'t>) = lock lc.LockObj lc.fn
// Expression builder for a locked context
type LockContext () =
member internal this.SyncRoot = obj()
member this.Return(value: 'v) = value
member this.ReturnFrom(value: ThreadSafeVar<'t>) = value.Value
member __.Bind(value: ThreadSafeVar<'t>, fn: 't -> 'u) = fn value.Value
// returns a CriticalSection
member this.Run(fn : unit -> 'u) = { LockObj = this.SyncRoot
fn=fn }
.
.
.

由于Bind,从锁上下文中读取线程安全值非常简单。例如


let lockedInt = ThreadSafeVar(1) // create a thread-safe variable
let context = LockContext()
let wrapperVal = context {
let! i = lockedInt // get the wrapper value inside lockedInt 
return i 
} |> CriticalSection.Lock

但是我正在努力理解如何实现从LockContext实例中设置值的方法。因此,我采用的方法是实现一个自定义操作,例如,称为setVal。到目前为止,我已经尝试过了,但我担心它们只是把水搅浑了。似乎自定义操作操作到目前为止在表达式内构建的计算,编码为元组,但我不认为在我的情况下需要这样做。

任何提示,指向资源,或直接帮助将不胜感激。

我不确定这样做是否明智,但我提出了一些基于State单子的方法,可能对您有用。首先,定义一个"有状态的"函数作为接受ThreadSafeVar并返回某种类型结果的函数:

ThreadSafeVar<'state> -> 'result

然后将该签名放入表示有状态计算的类型中:

type Stateful<'state, 'result> =
MkStateful of (ThreadSafeVar<'state> -> 'result)

现在我们需要一种方法来安全地运行这样的计算,使用给定的TSV:

let run (tsv : ThreadSafeVar<_>) (MkStateful f) =
lock tsv (fun () -> f tsv)

请注意,我已经摆脱了你的CriticalSection类型,而只是锁定TSV本身。

接下来,我们需要一种将纯值提升到有状态计算的方法:
let lift value =
MkStateful (fun _ -> value)

以及将两个有状态计算绑定在一起的方法:

let bind binder stateful =
MkStateful (fun tsv ->
run tsv stateful
|> binder
|> run tsv)

定义构建器是很简单的:

type LockContext () =
member __.Return(value) = lift value
member __.Bind(stateful, binder) = bind binder stateful
let context = LockContext()

我们还需要辅助计算来安全地设置和获取值:

let getValue =
MkStateful (fun tsv ->
tsv.Value)
let setValue value =
MkStateful (fun tsv ->
tsv.Value <- value)

把所有这些放在一起,我们可以定义一个增加TSV值的计算:

let comp =
context {
let! oldValue = getValue
let newValue = oldValue + 1
do! setValue newValue
return newValue
}

我们可以这样运行:

let lockedInt = ThreadSafeVar(1)
let result = comp |> run lockedInt
printfn "%A" result   // output is: 2

你可以在这里看到完整的解决方案并自己尝试。

最新更新