我正在使用带有RCWs的COM库。我总是发现,在超出范围之前手动释放任何非托管资源和垃圾收集是最佳做法。我不希望这个"慢下来"我的应用程序,如果在应用程序退出之前实际完成,我就不在乎了。所以我有这个方法:
// best effort:
internal static void CleanupComObjects(params object[] toRelease)
{
Task.Run(() =>
{
var t = Task.WhenAll(toRelease.Select((x) =>
Task.Run(() =>
System.Runtime.InteropServices.Marshal.ReleaseComObject(x))));
t.Wait();
System.GC.Collect();
});
}
没有我的API /应用程序的任何客户端或用户知道或关心异步代码正在运行,只要它不会产生除调用者所期望的任何副作用,这样做是否可行,或者有什么我可以避免哪些可能会导致意想不到的问题? (< - To"主要基于意见"关闭选民,请注意最后一行。)
答案 0 :(得分:5)
我不关心这个实际上是什么时候完成,如果有的话,在应用程序退出之前
这句话似乎与你的另一句话相冲突:
我总是发现在超出范围之前手动释放任何非托管资源和垃圾收集是最佳做法
如果你真的不在乎,在我看来你可以让.NET管理COM对象的生命周期,就像RCW打算完成的那样。
那就是说,我认为你的方法本身没有任何问题。实施对我来说似乎很苛刻,但在许多其他情况下会出现这种“即发即忘”的方法。你甚至可以说.NET的基本垃圾收集算法就是一个例子(毕竟,你通常不知道或无法控制它何时或如何发生......它“只是”)。
就个人而言,我会以更简化的方式编写它:
internal static async Task CleanupComObjects(params object[] toRelease)
{
await Task.WhenAll(toRelease.Select((x) =>
Task.Run(() =>
System.Runtime.InteropServices.Marshal.ReleaseComObject(x))));
System.GC.Collect();
}
甚至:
internal static Task CleanupComObjects(params object[] toRelease)
{
return Task.Run(() =>
{
foreach (object o in toRelease)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(o);
}
System.GC.Collect();
});
}
(从方法中返回Task
使调用者有机会观察操作的完成。这不是必需的,但是您可能会发现前进有充分的理由,例如,以便您可以检测到异常可能会发生。)
换句话说,我不清楚为什么要同时调用ReleaseComObject()
。根据这些物体的来源和公寓模型的不同,您可能会因为尝试这样做而遇到麻烦。
说到这里,你问题中缺少的另一个细节是你在这里处理的公寓模型。如果这些是STA对象,那么您的操作将被.NET编组回到拥有的线程,这意味着任何尝试与该线程上运行的任何其他代码同时释放它们(无论是与该对象或其他调用无关的其他代码)到ReleaseComObject()
)将毫无意义。您可以启动发布并发,但是发布操作和该线程中的其他代码都将被序列化。
所有这一切......
通常,我更喜欢编写解决问题的代码。您是否正在尝试解决此处发生的特定问题?代码已经有点不规则了,因为您不依赖于.NET的常规GC管理来处理您的对象。然后通过将它们的显式管理转移到其他线程来增加这种不规则性,根据您正在处理的COM对象的类型,这些线程甚至可能不成功。
我希望你有充分理由做所有这些工作。但那是什么原因呢?你试图解决的具体问题是什么?您是否能够确认此类代码确实解决了这个问题?
在我看来,这些都是重要的问题,但上面的帖子中没有足够的信息来解决这些问题。所以我鼓励你自己调查一下。您可能需要考虑一个单独的Stack Overflow问题,在该问题中,您可以提供可靠地再现您尝试解决的问题的良好Minimal, Complete, and Verifiable code example,以寻求解决该问题的替代方法的建议。