我正在使用 FSharp.Collections.ParallelSeq
和retry computation编写刮板。我想从多个页面并行检索HTML,并且我想在失败时重试请求。
例如:
open System
open FSharp.Collections.ParallelSeq
type RetryBuilder(max) =
member x.Return(a) = a // Enable 'return'
member x.Delay(f) = f // Gets wrapped body and returns it (as it is)
// so that the body is passed to 'Run'
member x.Zero() = failwith "Zero" // Support if .. then
member x.Run(f) = // Gets function created by 'Delay'
let rec loop(n) =
if n = 0 then failwith "Failed" // Number of retries exceeded
else try f() with _ -> loop(n-1)
loop max
let retry = RetryBuilder(4)
let getHtml (url : string) = retry {
Console.WriteLine("Get Url")
return 0;
}
//A property/field?
let GetHtmlForAllPages =
let pages = {1 .. 10}
let allHtml = pages |> PSeq.map(fun x -> getHtml("http://somesite.com/" + x.ToString())) |> Seq.toArray
allHtml
[<EntryPoint>]
let main argv =
let htmlForAllPages = GetHtmlForAllPages
0 // return an integer exit code
当我尝试与
GetHtmlForAllPages
中的main
交互时,该代码似乎挂起。逐步执行代码向我展示PSeq.map
开始对pages
的前四个值起作用。这是怎么回事导致
retry
计算表达式无法启动/完成? PSeq
和retry
之间有一些奇怪的相互作用吗?如果我使
GetHtmlForAllPages
为函数并调用它,则该代码将按预期工作。我很好奇GetHtmlForAllPages
是字段时会发生什么? 最佳答案
看起来您在静态构造函数中陷入僵局。该场景描述为here:
CLR使用内部锁来确保静态构造函数:
类或访问任何静态成员之前。
具有这种行为
CLR,如果我们执行任何操作,都有可能陷入僵局
静态构造函数中的异步阻塞操作。 (...)
主线程将等待助手线程在内部完成
静态构造函数。由于助手线程正在访问实例
方法,它将首先尝试获取内部锁。作为内部
锁已经被主线程获取了,我们将结束于
僵局情况。
在静态构造函数中使用Parallel LINQ(或任何其他类似的库,例如FSharp.Collections.ParallelSeq)会使您遇到该问题。
不幸的是,您为
GetHtmlForAllPages
值获得的是编译器生成的类的静态构造函数。从ILSpy(使用C#格式):namespace <StartupCode$ConsoleApplication1>
{
internal static class $Program
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal static readonly Program.RetryBuilder retry@17;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal static readonly int[] GetHtmlForAllPages@24;
[DebuggerBrowsable(DebuggerBrowsableState.Never), DebuggerNonUserCode, CompilerGenerated]
internal static int init@;
static $Program()
{
$Program.retry@17 = new Program.RetryBuilder(4);
IEnumerable<int> pages = Operators.OperatorIntrinsics.RangeInt32(1, 1, 10);
ParallelQuery<int> parallelQuery = PSeqModule.map<int, int>(new Program.allHtml@26(), pages);
ParallelQuery<int> parallelQuery2 = parallelQuery;
int[] allHtml = SeqModule.ToArray<int>((IEnumerable<int>)parallelQuery2);
$Program.GetHtmlForAllPages@24 = allHtml;
}
}
}
并在您的实际Program
类中:[CompilationMapping(SourceConstructFlags.Value)]
public static int[] GetHtmlForAllPages
{
get
{
return $Program.GetHtmlForAllPages@24;
}
}
那就是僵局的来源。一旦将
GetHtmlForAllPages
更改为一个函数(通过添加()
),它就不再是该静态构造函数的一部分,这使程序可以按预期运行。关于f# - 为什么带有计算表达式的PSeq.map似乎挂起?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43087426/