是否有人在F#异步代码中使用设置Thread.CurrentPrincipal
进行身份验证?
是否有任何陷阱需要注意,或者只是按预期工作?
答案 0 :(得分:4)
这听起来不是一个好主意,因为Thread.CurrentPrincipal
依赖于与给定线程关联的IPrincipal
,但在异步工作流内,您可能无法在同一个线程上运行所有时间。
考虑这个例子:
open System.Threading
let collectThreadInformation jobId =
async {
let tId1 = Thread.CurrentThread.ManagedThreadId
do! Async.Sleep 1000
let tId2 = Thread.CurrentThread.ManagedThreadId
return jobId, tId1, tId2 }
这是一个简单的异步工作流,可在睡眠前后报告线程ID一秒钟。以下是并行运行其中10个的看法:
> [1 .. 10]
|> List.map collectThreadInformation
|> Async.Parallel
|> Async.RunSynchronously;;
val it : (int * int * int) [] =
[|(1, 15, 15); (2, 14, 15); (3, 15, 15); (4, 15, 15); (5, 15, 15);
(6, 15, 15); (7, 15, 15); (8, 14, 15); (9, 15, 15); (10, 15, 15)|]
正如您所看到的,某些线程ID在Sleep
之前和之后发生了变化,例如第二个元组:(2, 14, 15)
。在Sleep
运行主题14 之前,但在睡眠之后,它在主题15 上重新启动。
我认为将IPrincipal
实例作为函数参数传递会更安全。
答案 1 :(得分:1)
我在不同的地方对此有各种相互矛盾的回应,所以我实际上构建了一个测试程序。运行下面的代码表明,当async
工作流在ThreadPool上运行时,它们确实保留了线程上下文。
module TestCurrentPrinciple
open System
open System.Security.Principal
open System.Threading
let rand = Random()
let spawnWorkflow userName =
async {
let identity = GenericIdentity(userName)
let principle = GenericPrincipal(identity, [||])
Thread.CurrentPrincipal <- principle
for i in 1..10000 do
let principleName = Thread.CurrentPrincipal.Identity.Name
if principleName <> userName then
failwithf "Mismatch! Principle name %s does not match username %s (Iteration %d)"
principleName userName i
do! Async.Sleep(rand.Next(10))
}
let names =
[ "mavnn"; "ploeh"; "dsyme"; "biboudis"; "MattDrivenDev"; "fssnip"; "marprz_93"; "skillsmatter"; "thinkb4coding" ]
printfn "Starting"
names
|> List.map spawnWorkflow
|> Async.Parallel
|> Async.RunSynchronously
|> ignore
printfn "Done"
Console.ReadLine() |> ignore
这对我目前的项目可能很有意思。