Jos*_*rty 2 f# computation-expression
我正在编写一个使用FSharp.Collections.ParallelSeq和重试计算的刮刀.我想从多个页面并行检索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
Run Code Online (Sandbox Code Playgroud)
当我尝试互动GetHtmlForAllPages从main代码似乎挂起.单步执行代码会显示我PSeq.map开始处理前四个值pages.
发生了什么导致retry计算表达式永远不会开始/完成?有没有之间一些奇怪的相互作用PSeq和retry?
如果我创建GetHtmlForAllPages一个函数并调用它,代码将按预期工作.我很好奇当GetHtmlForAllPages一个领域发生了什么?
看起来你在静态构造函数中死锁.场景描述在这里:
CLR使用内部锁来确保静态构造函数:
- 只被叫一次
- 在创建任何类的实例之前或访问任何静态成员之前执行.
有了CLR的这种行为,如果我们在静态构造函数中执行任何异步阻塞操作,就有可能出现死锁.(......)
主线程将等待辅助线程在静态构造函数中完成.由于辅助线程正在访问实例方法,它将首先尝试获取内部锁.由于主线程已经获取了内部锁定,因此我们将陷入死锁状态.
在静态构造函数中使用并行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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
在你的实际Program课程中:
[CompilationMapping(SourceConstructFlags.Value)]
public static int[] GetHtmlForAllPages
{
get
{
return $Program.GetHtmlForAllPages@24;
}
}
Run Code Online (Sandbox Code Playgroud)
这就是僵局的来源.
一旦你改成GetHtmlForAllPages一个函数(通过添加())它就不再是那个静态构造函数的一部分,这使得程序按预期工作.
| 归档时间: |
|
| 查看次数: |
99 次 |
| 最近记录: |