【问题标题】:In TPL Dataflow, is it possible to change DataflowBlockOptions after block is created but before it is used?在 TPL Dataflow 中,是否可以在创建块之后但在使用之前更改 DataflowBlockOptions?
【发布时间】:2014-07-01 11:14:00
【问题描述】:

...生效了吗?

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

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

但是……我并没有忘记这些属性具有公共设置器。那么......我可以使用 ExecutionDataflowBlockOptions 的占位符实例创建块并保留它,以便稍后在将块链接在一起时设置 SingleProducerConstrained=true (并且它将生效)?

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

更新: @i3amon 在他的回答中正确指出这是无法完成的,因为数据流块会克隆您传入的 DataflowBlockOptions 并使用它。但我还是这样做了,使用我可以通过反射和动态访问的内部数据结构。我把它放在下面的答案中。

【问题讨论】:

  • SingleProducerConstrained 确实有效果,但仅限于特定情况。例如,在 ActionBlock 中,动作必须是同步的才能计数。
  • 就我而言,我正在处理同步操作。它是一个没有异步操作的计算绑定管道。之前的 SO 回答 here 表明它确实可以在这种情况下帮助提高吞吐量。

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


【解决方案1】:

这是不可能的。事后修改选项是行不通的。这些选项被克隆在块的构造函数中。稍后更改选项将无效。

您可以查看herehere 的示例,并且很容易验证:

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

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

输出:

酒吧

【讨论】:

  • 再次感谢 i3amon。事实上,就在看到你的答案之前,我只是看了dotnetinside.com(你在我上一个问题的答案中教我的!),发现它被克隆了。
【解决方案2】:

让我回答我自己的问题。使用来自 DotNetInside 对 Dataflow 程序集的反编译的信息,例如,TransformBlockhere(再次感谢 @i3amon 提供指向 dotnetinside.com 的链接),以及非常好的 ExposedObjectcodeplex here(我了解到在this blog post,我做了以下事情:

  • TPL 数据流块都通过 DebuggerTypeProxy 属性实现调试器可视化工具,该属性应用于一个类型,在要显示原始类型时命名另一个类型以在 Visual Studio 调试器中使用(例如,watch窗口)。

  • 每个DebuggerTypeProxy命名的类都是属性附加到的数据流块的内部类,通常命名为DebugView。该类始终是私有的和密封的。它公开了有关数据流块的许多很酷的东西,包括其真实(非副本)DataflowBlockOptions 以及 - 如果该块是源块 - 一个 ITargetBlock[],可用于从一开始就跟踪数据流网络构建后阻塞。

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

  • 1234563可以更改其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 });
    }
    ...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-29
    • 1970-01-01
    • 2011-12-05
    • 1970-01-01
    • 2018-07-08
    • 1970-01-01
    • 2021-10-04
    相关资源
    最近更新 更多