在TPL Dataflow中,是否可以在创建块之后但在使用之前更改DataflowBlockOptions?

时间:2014-06-28 20:07:44

标签: c# .net task-parallel-library tpl-dataflow

......让它生效吗?

我想推迟设置ExecutionDataflowBlockOptions.SingleProducerConstrained属性,直到我准备好将网络链接在一起。 (因为,我想用它们的语义分开创建块,用它的语义将网络链接在一起。)

但据我所知,你只能在创建块时设置ExecutionDataflowBlockOptions(例如,对于TransformBlock,TransformManyBlock等,你将它传递给构造函数,否则它是不可见的。)

然而......我没有注意到这些物业有公共制定者。所以...我可以使用ExecutionDataflowBlockOptions的占位符实例创建块并保持它以便以后我可以设置SingleProducerConstrained = true,如果我希望,将块链接在一起(并且它将生效)?

(顺便说一下,有没有什么方法可以判断SingleProducerConstrained除了测量吞吐量之外是否有任何影响?)

更新: @ i3amon在他的回答中正确地指出,由于数据流阻止克隆您传入的DataflowBlockOptions并使用它,因此无法完成此操作。但无论如何我使用内部数据结构,我可以通过反射和动态访问。我把它放在下面的答案中。

2 个答案:

答案 0 :(得分:3)

这是不可能的。在事实之后修改选项将不起作用。选项被克隆在块的构造函数中。稍后更改选项将不起作用。

您可以看到herehere的示例,并且验证很简单:

var options = new ExecutionDataflowBlockOptions
{
    NameFormat = "bar",
};
var block = new ActionBlock<int>(_ => { }, options);

options.NameFormat = "hamster";
Console.WriteLine(block.ToString());

输出:

  

答案 1 :(得分:1)

让我回答我自己的问题。使用来自DotNetInside的Dataflow程序集反编译的信息,例如TransformBlock here(再次感谢@ i3amon获取dotnetinside.com的链接),以及ExposedObject包中的DebuggerTypeProxy包。 3}}(我在codeplex here学到了什么,我做了以下几点:

  • TPL Dataflow通过DebuggerTypeProxy属性阻止所有实现调试器可视化工具,应用于类型时,只要显示原始类型,就会在Visual Studio调试器中命名另一种类型(例如,观看窗口)。

  • 这些DebugView命名类中的每一个都是属性附加到的数据流块的内部类,通常名为DataflowBlockOptions。那堂课总是私密而密封的。它暴露了许多关于数据流块的很酷的东西,包括它的真实(不是副本)ITargetBlock[]以及 - 如果块是源块 - DebugView,它可用于跟踪数据流网络从建设后的起始区块开始。

  • 获得dynamic的实例后,您可以ExposedObject通过ExposedObject获取该类公开的任何属性 - DataflowBlockOptions让您获取一个对象并使用普通方法和属性语法来访问其方法和属性。

  • 因此,您可以从数据流块中获取NameFormat并更改其ExecutionDataflowBlockOptions,如果它是SingleProducerConstrained(并且您还没有联系到阻止到其他块)您可以更改其dynamic值。

  • 但是,您无法使用DebugView来查找或构造内部DebuggerTypeProxy类的实例。你需要反思。首先从您的// Set (change) the NameFormat of a dataflow block after construction public void SetNameFormat(IDataflowBlock block, string nameFormat) { try { dynamic debugView = block.GetInternalData(Logger); if (null != debugView) { var blockOptions = debugView.DataflowBlockOptions as DataflowBlockOptions; blockOptions.NameFormat = nameFormat; } } catch (Exception ex) { ... } } // Get access to the internal data of a dataflow block via its DebugTypeProxy class public static dynamic GetInternalData(this IDataflowBlock block) { Type blockType = block.GetType(); try { // Get the DebuggerTypeProxy attribute, which names the debug class type. DebuggerTypeProxyAttribute debuggerTypeProxyAttr = blockType.GetCustomAttributes(true).OfType<DebuggerTypeProxyAttribute>().Single(); // Get the name of the debug class type string debuggerTypeProxyNestedClassName = GetNestedTypeNameFromTypeProxyName(debuggerTypeProxyAttr.ProxyTypeName); // Get the actual Type of the nested class type (it will be open generic) Type openDebuggerTypeProxyNestedClass = blockType.GetNestedType( debuggerTypeProxyNestedClassName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic); // Close it with the actual type arguments from the outer (dataflow block) Type. Type debuggerTypeProxyNestedClass = openDebuggerTypeProxyNestedClass.CloseNestedTypeOfClosedGeneric(blockType); // Now create an instance of the debug class directed at the given dataflow block. dynamic debugView = ExposedObject.New(debuggerTypeProxyNestedClass, block); return debugView; } catch (Exception ex) { ... return null; } } // Given a (Type of a) (open) inner class of a generic class, return the (Type // of the) closed inner class. public static Type CloseNestedTypeOfClosedGeneric( this Type openNestedType, Type closedOuterGenericType) { Type[] outerGenericTypeArguments = closedOuterGenericType.GetGenericArguments(); Type closedNestedType = openNestedType.MakeGenericType(outerGenericTypeArguments); return closedNestedType; } // A cheesy helper to pull a type name for a nested type out of a full assembly name. private static string GetNestedTypeNameFromTypeProxyName(string value) { // Expecting it to have the following form: full assembly name, e.g., // "System.Threading...FooBlock`1+NESTEDNAMEHERE, System..." Match m = Regex.Match(value, @"^.*`\d+[+]([_\w-[0-9]][_\w]+),.*$", RegexOptions.IgnoreCase); if (!m.Success) return null; else return m.Groups[1].Value; } // Added to IgorO.ExposedObjectProject.ExposedObject class to let me construct an // object using a constructor with an argument. public ExposedObject { ... public static dynamic New(Type type, object arg) { return new ExposedObject(Create(type, arg)); } private static object Create(Type type, object arg) { // Create instance using Activator object res = Activator.CreateInstance(type, arg); return res; // ... or, alternatively, this works using reflection, your choice: Type argType = arg.GetType(); ConstructorInfo constructorInfo = GetConstructorInfo(type, argType); return constructorInfo.Invoke(new object[] { arg }); } ... } 属性中删除 dataflow块的类型,获取调试类的名称,假设它是一个内部类 数据流块的类型并搜索它,将其转换为封闭的泛型类型,最后 构建一个实例。

  • 请充分注意您正在使用数据流内部的未记录代码。用你自己的 判断这是否是一个好主意。在我看来,TPL Dataflow的开发人员做了很多工作来支持在调试器中查看这些块,他们可能会继续保持这种状态。细节可能会发生变化,但是,如果您正在对这些类型的反射和动态使用进行适当的错误检查,您将能够发现代码何时停止使用新版本的TPL Dataflow。

以下代码片段可能不会一起编译 - 它们只是从我的工作代码中剪切和粘贴,来自不同的类,但它们肯定会给你这个想法。我做得很好。 (另外,为了简洁起见,我省略了所有错误检查。)(另外,我使用版本4.5.20.0开发/测试了此代码仅TPL数据流,因此您可能必须根据过去或未来的版本进行调整。)< / p>

{{1}}