C# 是什么阻止对象被处置?/异步流是否使对象保持活动状态?

C# 是什么阻止对象被处置?/异步流是否使对象保持活动状态?,c#,asynchronous,garbage-collection,C#,Asynchronous,Garbage Collection,考虑以下代码: public class ChainHandler { public void ProcessTopUp(string amount, string phone, ResponseCallback responseCallback) { var statebag = GetStateBag(); ICommand basecommand = new BlmLoginCommand(statebag); baseco

考虑以下代码:

public class ChainHandler
{
    public void ProcessTopUp(string amount, string phone, ResponseCallback responseCallback)
    {
        var statebag = GetStateBag();

        ICommand basecommand = new BlmLoginCommand(statebag);
        basecommand .SetNext(new BlmDynamicCommand(statebag, RequestMessageEvent.TCD_RecargaTAE10, new Dictionary<string, string> { { "amt", amount }, { "cel", phone } }))
               .SetNext(new BlmDynamicCommand(statebag, RequestMessageEvent.TCD_BuyProduct))
               .SetNext(new BlmEndCommand(statebag, responseCallback));

        basecommand.ExecuteAsync();
    }


}

class Program
{
    static void Main(string[] args)
    {
        ChainHandler ch = new ChainHandler();

        ch.ProcessTopUp("5.00", "0123456789", OnProcessResponse);

        Console.ReadLine();
    }

    private static void OnProcessResponse()
    {
        //...
    }
}
ExecuteAsync方法启动使用命令的异步流。 这些命令在内部使用套接字类异步基于事件的异步模式

问题是:是否有可能因为对象basecommand在离开ExecuteAsync方法后被释放而永远无法到达EndCommand

甚至.net都知道EndCommand有一个回调方法

或者.net是否足够智能,可以在异步流完成之前保持对象的活动状态


如果对象不保持活动状态,我可以做什么来指示垃圾收集器不要处理basecommand?

在您的情况下,您的命令将不会被处理,因为它具有委托/回调引用。这意味着,只要委托存在,您的命令对象就会被委托保持活动状态。最终,在执行回调之后,GC将拾取没有根的未触及对象,并相应地进行处理

更新:

另一种情况是,如果没有回调引用该怎么办


在这种情况下,您的对象不适合立即进行垃圾收集,因为您在代码API中遵循的EPM模式在线程池中保留对您的请求的引用,特别是IO完成线程。这提示GC在完成之前不要触摸或收集。

在您的情况下,您的命令将不会被释放,因为它有一个委托/回调引用。这意味着,只要委托存在,您的命令对象就会被委托保持活动状态。最终,在执行回调之后,GC将拾取没有根的未触及对象,并相应地进行处理

更新:

另一种情况是,如果没有回调引用该怎么办


在这种情况下,您的对象不适合立即进行垃圾收集,因为您在代码API中遵循的EPM模式在线程池中保留对您的请求的引用,特别是IO完成线程。这提示GC在完成之前不要触摸或收集。

这里的准确性很重要。对象不会自动处置。它总是在代码中显式完成,可以通过using语句,也可以调用Dispose方法。因此,如果不在代码中进行处理,就永远不会有问题

你可能想说的是垃圾收集。这在.NET中非常简单,只要垃圾收集器可以看到对对象的引用,并且该引用由另一个自身处于活动状态的对象持有,那么就不会收集对象

然而,并不总是那么清楚的是引用存储在哪里。C编译器可以重写代码,并将其从类中的方法移动到隐藏类的方法。该隐藏类有一个无法说出的名称,并有一个存储引用的字段。垃圾收集器可以看到,从而阻止对象被收集。将引用从代码复制到隐藏类对象字段的技术术语是


这种代码重写技巧在C语言中的许多地方都使用。最初是用支持匿名方法和迭代器的语言实现的。并在以后的版本中扩展以实现lambda表达式和异步方法。查看发生了什么的一个好方法是查看C编译器生成的代码。使用ildasm.exe或不完善的反编译器可见。否则,这当然符合“足够聪明”的绰号。

准确在这里很重要。对象不会自动处置。它总是在代码中显式完成,可以通过using语句,也可以调用Dispose方法。因此,如果不在代码中进行处理,就永远不会有问题

你可能想说的是垃圾收集。这在.NET中非常简单,只要垃圾收集器可以看到对对象的引用,并且该引用由另一个自身处于活动状态的对象持有,那么就不会收集对象

然而,并不总是那么清楚的是引用存储在哪里。C编译器可以重写代码,并将其从类中的方法移动到隐藏类的方法。该隐藏类有一个无法说出的名称,并有一个存储引用的字段。垃圾收集器可以看到,从而阻止对象被收集。将引用从代码复制到隐藏类对象字段的技术术语是

这种代码重写技巧在C语言中的许多地方都使用。最初是用支持匿名方法和迭代器的语言实现的。并在以后的版本中扩展以实现lambda表达式和异步方法。查看发生了什么的一个好方法是查看C编译器生成的代码。使用ildasm.exe或不完善的反编译器可见。这
否则肯定符合足够聪明的绰号。

GC太聪明了,无法指导它做任何事情。只要您的程序的任何代码使用它,它就会使您的对象保持活动状态。如果您在异步程序流中,对象可以被释放。这就是为什么在异步套接字过程中回调无法再找到时会出现对象释放异常的原因。。。。您是否在代码中看到错误,或者您是否要求确认?尽管如此,汉斯是对的:区分处置和收集是非常重要的。在第二种情况下,GC足够智能,当它仍然可以使用时,不会收集任何东西。这包括异步执行、线程池中的执行等。Hans再次正确地指出,即使您看不到它,编译器也会插入代码来传递引用,这将使对象在需要时保持活动状态……在第一种情况下,这取决于情况,完全掌握在您使用的框架手中。在BlmEndCommand有机会运行并调用Callback之前,没有任何东西禁止在某处调用Dispose。GC太聪明了,无法指示它任何事情。只要您的程序的任何代码使用它,它就会使您的对象保持活动状态。如果您在异步程序流中,对象可以被释放。这就是为什么在异步套接字过程中回调无法再找到时会出现对象释放异常的原因。。。。您是否在代码中看到错误,或者您是否要求确认?尽管如此,汉斯是对的:区分处置和收集是非常重要的。在第二种情况下,GC足够智能,当它仍然可以使用时,不会收集任何东西。这包括异步执行、线程池中的执行等。Hans再次正确地指出,即使您看不到它,编译器也会插入代码来传递引用,这将使对象在需要时保持活动状态……在第一种情况下,这取决于情况,完全掌握在您使用的框架手中。在BlmEndCommand有机会运行并调用回调之前,如果回调委托不在那里,没有任何东西禁止在某处调用Dispose。如果整个流程将完全在Chainhandler中处理。我不确定您的假设是否正确。如果任何活动线程包含对对象的引用,或者在这两种情况下都直接或间接静态引用对象,则该对象被视为活动对象,不符合垃圾收集的条件。在您的示例中,EPMAPI在线程池中保留对您的请求的引用,特别是提交IO完成操作,因此在完成之前不会对其进行垃圾收集,无论它是否具有回调。这对我来说是一个新的信息。这是否适用于所有类型的异步模式,或者仅当涉及IO完成端口时才适用。所有异步操作都在作为CLR线程池一部分的IO完成端口线程上执行。但是使用async/await的最新版本的异步操作使用了更多的状态机。我并不完全了解async/await在内存管理方面的作用。因此,您可以更好地寻求外部帮助。如果回调委托不在那里。如果整个流程将完全在Chainhandler中处理。我不确定您的假设是否正确。如果任何活动线程包含对对象的引用,或者在这两种情况下都直接或间接静态引用对象,则该对象被视为活动对象,不符合垃圾收集的条件。在您的示例中,EPMAPI在线程池中保留对您的请求的引用,特别是提交IO完成操作,因此在完成之前不会对其进行垃圾收集,无论它是否具有回调。这对我来说是一个新的信息。这是否适用于所有类型的异步模式,或者仅当涉及IO完成端口时才适用。所有异步操作都在作为CLR线程池一部分的IO完成端口线程上执行。但是使用async/await的最新版本的异步操作使用了更多的状态机。我并不完全了解async/await在内存管理方面的作用。因此,你可以更好地寻求外部帮助。