在 TPL 数据流中,是否可以在创建块之后但在使用块之前更改数据流块选项
本文关键字:数据流 选项 之后 是否 创建 TPL | 更新日期: 2023-09-27 18:10:51
...并生效吗?
我想推迟设置 ExecutionDataflowBlockOptions.SingleProducerConstrained 属性,直到我准备好将网络链接在一起。 (因为,我想将创建块及其语义与将网络链接在一起及其语义分开。
但据我所知,您只能在创建块时设置 ExecutionDataflowBlockOptions(例如,对于 TransformBlock、TransformManyBlock 等,您将其传递给构造函数,否则它不可见(。
然而。。。我并没有逃脱我的注意,这些房产有公共二传手。 所以。。。我是否可以使用 ExecutionDataflowBlockOptions 的占位符实例创建块并保留它,以便以后可以在将块链接在一起时根据需要设置 SingleProducerConscured=true(并且它将生效(?
(顺便说一句,除了测量吞吐量之外,有没有办法判断 SingleProducerConstrained 是否具有任何影响?
更新:@i3amon在他的回答中正确地指出了这是不可能的,因为数据流块克隆了你传入并使用它DataflowBlockOptions
。 但我还是做到了,使用我可以通过反射和动态访问的内部数据结构。 我把它放在下面的答案中。
这是不可能的。事后修改选项将不起作用。这些选项在块的构造函数中克隆。稍后更改选项将不起作用。
您可以在此处和此处查看示例,并且验证起来很简单:
var options = new ExecutionDataflowBlockOptions
{
NameFormat = "bar",
};
var block = new ActionBlock<int>(_ => { }, options);
options.NameFormat = "hamster";
Console.WriteLine(block.ToString());
输出:
酒吧
让我回答我自己的问题。 例如,使用来自 DotNetInside 对数据流程序集的反编译的信息,TransformBlock
这里(再次感谢@i3amon指向 dotnetinside.com 的链接(,以及 codeplex 上非常好的ExposedObject
包(我在这篇博文中了解到这一点,我做了以下工作:
-
TPL 数据流通过
DebuggerTypeProxy
属性阻止所有实现调试器可视化工具,该属性应用于一个类型,命名要在 Visual Studio 调试器中使用的另一种类型,只要要显示原始类型(例如,监视窗口(。 -
这些
DebuggerTypeProxy
命名类中的每一个都是属性附加到的数据流块的内部类,通常名为DebugView
。 该类始终是私有和密封的。 它公开了许多关于数据流块的很酷的东西,包括它的正版(不是副本(DataflowBlockOptions
,以及 - 如果块是源块 - 一个ITargetBlock[]
,可用于在构建后从其起始块跟踪数据流网络。 -
获得
DebugView
的实例后,可以通过ExposedObject
使用dynamic
来获取类公开的任何属性 -ExposedObject
允许您获取对象并使用普通方法和属性语法来访问其方法和属性。 -
因此,您可以从数据流块中获取
DataflowBlockOptions
并更改其NameFormat
,如果它是一个ExecutionDataflowBlockOptions
(并且您尚未将块连接到其他块(,则可以更改其SingleProducerConstrained
值。 -
但是,不能使用
dynamic
查找或构造内部DebugView
类的实例。 你需要对此进行反思。 您首先DebuggerTypeProxy
从您的数据流块的类型,获取调试类的名称,假设它是数据流块的类型并搜索它,将其转换为封闭泛型类型,最后构造实例。 -
请注意,你使用的是数据流内部的未记录代码。 使用您自己的判断这是否是一个好主意。 在我看来,TPL Dataflow 的开发人员做了很多工作来支持在调试器中查看这些块,他们可能会继续这样做。 详细信息可能会更改,但是,如果对这些类型的反射和动态使用进行适当的错误检查,则可以发现代码何时停止使用新版本的 TPL 数据流。
以下代码片段可能不会一起编译 - 它们只是从我的工作代码中剪切和粘贴出来,来自不同的类,但它们肯定会给你这个想法。 我让它工作正常。 (另外,为了简洁起见,我省略了所有错误检查。 (此外,我仅使用 TPL 数据流的 4.5.20.0 版本开发/测试了此代码,因此您可能需要针对过去或将来的版本对其进行调整。
// 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 });
}
...
}