链接是否保持链接块活着?

时间:2018-03-22 18:16:14

标签: c# tpl-dataflow

使用<form action="#" id="form" class="form-horizontal" onsubmit="save();"> <!-- Form stuff --> <div class="modal-footer"> <input type="submit" id="btnSave" class="btn btn-primary" value="save"> <button type="button" class="btn btn-danger" data-dismiss="modal">Cancel</button> </div> </form> 时,如果我将阻止System.Threading.Tasks.Dataflow与阻止a相关联,链接会保持b还活着吗?或者我是否需要保留对b周围的引用以防止它被收集?

b

3 个答案:

答案 0 :(得分:3)

您将变量与变量内容混淆。他们的生命周期可以完全不同。

一旦控件离开块,局部变量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来保持活着。

答案 1 :(得分:2)

除了@Eric对GC行为的重要解释之外,我想解决与TPL-Dataflow相关的特殊情况。您可以轻松地查看LinkTo从简单测试中获得的行为。请注意,据我所知,除了b的链接外,没有任何内容可以保留a

[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;
    }
}

答案 2 :(得分:1)

是的,链接一个数据流块足以防止对其进行垃圾回收。不仅如此,即使没有任何引用,仅通过工作即可完成工作,直到该块工作完成。这是一个runnable示例:

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);
    }
}

输出:

Processing an item
Countdown: 5
Processing an item
Countdown: 4
Processing an item
Countdown: 3
Processing an item
Countdown: 2
Processing an item
Countdown: 1
Processing an item
Shutting down
Press any key to continue . . .

只要该进程中还有一个前台线程,该块就会继续运行。