Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 链接是否使链接块保持活动状态?_C#_Tpl Dataflow - Fatal编程技术网

C# 链接是否使链接块保持活动状态?

C# 链接是否使链接块保持活动状态?,c#,tpl-dataflow,C#,Tpl Dataflow,使用System.Threading.Tasks.Dataflow时,如果我将blocka链接到blockb,链接会保持b活动吗?或者我是否需要保留对b的引用以防止收集 internal class SomeDataflowUser { public SomeDataflowUser() { _a = new SomeBlock(); var b = new SomeOtherBlock(); _a.LinkTo(b);

使用
System.Threading.Tasks.Dataflow
时,如果我将block
a
链接到block
b
,链接会保持
b
活动吗?或者我是否需要保留对
b
的引用以防止收集

internal class SomeDataflowUser
{
    public SomeDataflowUser()
    { 
        _a = new SomeBlock();
        var b = new SomeOtherBlock();
        _a.LinkTo(b);
    }

    public void ReactToIncomingMessage( Something data )
    {    
        // might b be collected here?
        _a.Post( data );
    }

    private ISourceBlock<Something> _a;
}
内部类SomeDataflowUser
{
公共数据流用户()
{ 
_a=新的SomeBlock();
var b=新的SomeOtherBlock();
_a、 链接至(b);
}
public void ReactToIncomingMessage(数据)
{    
//b可以在这里收集吗?
_a、 员额(数据);
}
私有ISourceBlock_a;
}

您将变量与变量内容混淆了。它们可以有完全不同的寿命

一旦控件离开块,局部变量
b
就不再是GC的根。存储在
b
中的引用引用的对象是一个托管对象,只要从根目录可以访问该对象,GC将使其保持活动状态

现在,请注意,允许GC在控件离开块之前将局部变量视为死变量。如果您有:

var a = whatever;
a.Foo(); 
var b = whatever;
// The object referred to by `a` could be collected here. 
b.Foo();
return;
因为例如,抖动可能决定
b
可以使用与
a
相同的本地存储,因为它们的用法不重叠只要
a
在范围内,就不要求
a
引用的对象保持活动状态。


如果对象的析构函数具有副作用,您需要延迟到块结束,则这可能会导致问题;当析构函数中有非托管代码调用时,尤其会发生这种情况。在这种情况下,使用keep-alive使其保持活动状态。

除了@Eric对GC行为的出色解释之外,我还想介绍与TPL数据流相关的特殊情况。您可以很容易地看到
LinkTo
从一个简单的测试中产生的行为。请注意,据我所知,除了指向
a
的链接之外,没有任何东西可以保留到
b

[TestFixture]
public class BlockTester
{

    private int count;

    [Test]
    public async Task Test()
    {
        var inputBlock = BuildPipeline();
        var max = 1000;
        foreach (var input in Enumerable.Range(1, max))
        {
            inputBlock.Post(input);
        }
        inputBlock.Complete();

        //No reference to block b
        //so we can't await b completion
        //instead we'll just wait a second since
        //the block should finish nearly immediately
        await Task.Delay(TimeSpan.FromSeconds(1));
        Assert.AreEqual(max, count);
    }

    public ITargetBlock<int> BuildPipeline()
    {
        var a = new TransformBlock<int, int>(x => x);
        var b = new ActionBlock<int>(x => count = x);
        a.LinkTo(b, new DataflowLinkOptions() {PropagateCompletion = true});
        return a;
    }
}
[TestFixture]
公共类块测试器
{
私人整数计数;
[测试]
公共异步任务测试()
{
var inputBlock=BuildPipeline();
var max=1000;
foreach(可枚举范围内的var输入(最大值为1))
{
inputBlock.Post(输入);
}
inputBlock.Complete();
//未提及b区
//所以我们不能等待b的完成
//相反,我们会等一等
//这个街区几乎马上就要结束了
等待任务延迟(时间跨度从秒(1));
Assert.AreEqual(最大值,计数);
}
公共ITargetBlock BuildPipeline()
{
var a=新的转换块(x=>x);
var b=新动作块(x=>count=x);
a、 链接到(b,新的DataflowLinkOptions(){PropagateCompletion=true});
返回a;
}
}

是的,链接数据流块足以防止它被垃圾收集。不仅如此,即使没有任何引用,只要有工作要做,块就可以一直存活,直到它的工作完成。以下是一个例子:


只要进程中有一个前台线程仍处于活动状态,块就会继续运行。

是否有什么东西使
a
保持活动状态
a
b
在这里似乎是未初始化的局部变量;它们是什么?你不相信垃圾收集器能正确地完成它的工作,有什么原因吗?你到底在问什么?@Eric:我的问题是关于数据流的内部工作:链接是否防止垃圾收集?@Lasse:“链接a到b”意味着使用参数
b
a
上调用
LinkTo
,这些都是托管对象。垃圾收集器的唯一工作是正确管理它们的生命周期。信任GC。@Eric:我信任GC,但我不知道
LinkTo
是否建立了GC可以识别的链接,或者它是否创建了弱引用。这并不是完全不可能的,想想看
PropertyObserver
或Prism的
EventAggregator
,两者都不能让订阅服务器保持活动状态。数据流可能会认为只有我有显式引用的块才被认为是活动的。我想,字段的情况会有所不同吗?当然,对于局部变量,必须使用像
GC.KeepAlive
这样的函数来保持它们不被收集。我意识到我的示例选择得不太好,可能会使事情变得复杂。@Haukinger:实例字段的存在时间与包含它们的对象的存在时间一样长。没错,所以我的问题是:我是否需要为每个块指定一个字段?
public static class Program
{
    static void Main(string[] args)
    {
        StartBlock();
        Thread.Sleep(500);
        for (int i = 5; i > 0; i--)
        {
            Console.WriteLine($"Countdown: {i}");
            Thread.Sleep(1000);
            GC.Collect();
        }
        Console.WriteLine("Shutting down");
    }

    static void StartBlock()
    {
        var block = new ActionBlock<int>(item =>
        {
            Console.WriteLine("Processing an item");
            Thread.Sleep(1000);
        });
        for (int i = 0; i < 10; i++) block.Post(i);
    }
}