.NET GC从一个终结访问同步对象对象、NET、GC

2023-09-06 22:34:47 作者:我就在你身后等你一个回头

最近我读了这篇文章安全线程同步因为我很好奇,有关从finaliser的呼叫的线程安全。我写了下面code,以测试从finaliser访问静态线程安全的集合。

I recently read this article Safe Thread Synchronization as I was curious about the thread safety of calls made from a finaliser. I wrote the following code to test access to a static thread safe collection from a finaliser.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GCThreadTest
{
    class Program
    {
        static class FinaliserCollection
        {
            private static Queue<int> s_ItemQueue = new Queue<int>();
            private static System.Object s_Lock = new System.Object();

            public static void AddItem(int itemValue)
            {
                lock(s_Lock)
                {
                    s_ItemQueue.Enqueue(itemValue);
                }
            }

            public static bool TryGetItem(out int item)
            {
                lock(s_Lock)
                {
                    if (s_ItemQueue.Count <= 0)
                    {
                        item = -1;
                        return false;
                    }

                    item = s_ItemQueue.Dequeue();
                    return true;
                }
            }
        }

        class FinaliserObject
        {
            private int m_ItemValue;

            public FinaliserObject(int itemValue)
            {
                m_ItemValue = itemValue;
            }

            ~FinaliserObject()
            {
                FinaliserCollection.AddItem(m_ItemValue);
            }
        }

        static void Main(string[] args)
        {
            int itemValueIn = 0;
            int itemValueOut = 0;

            while (itemValueOut < 10000)
            {
                System.Threading.ThreadPool.QueueUserWorkItem
                    (delegate(object value)
                    {
                        new FinaliserObject((int)value);

                        System.Threading.Thread.Sleep(5);

                    }, itemValueIn);

                itemValueIn = itemValueIn + 1;

                // This seems to stop finaliser from
                // being called?
                // System.Threading.Thread.Sleep(5);

                int tempItemValueOut = -1;
                if (FinaliserCollection.TryGetItem(out tempItemValueOut))
                    itemValueOut = tempItemValueOut;
            }

            System.Console.WriteLine("Finished after {0} items created", itemValueOut);
            System.Console.ReadLine();
        }
    }
}

如果没有while循环这个code似乎运行正常,但它与死​​锁真的安全了'睡眠'电话吗?难道永远是可能的,而排队的线程池项目正在访问静态集合必须作出一个finaliser电话吗?为什么加入睡眠主线程while循环会停止所有finalisers被称为?

Without the 'Sleep' call in the while loop this code seems to run fine but is it really safe from deadlocking? Would it ever be possible for a finaliser call to be made while a queued thread pool item is accessing the static collection? Why does adding the 'Sleep' to the main threads while loop appear to stop all finalisers from being called?

推荐答案

哇。什么......这是最离奇的一件code我见过。 @。@

Wow. What the... This is the most bizarre piece of code I've ever seen. @.@

首先,你指的是什么终结呼叫?我看到的唯一终结是终结为 FinaliserObject ,它会被调用10000次,并且可以独立于任何组织关于静态收集事情被调用。 I.E.是,这些对象可以在其它的目的是正在从集合出列被破坏。这不是一个问题。

First of all, what finalizer call are you referring to? The only finalizer I see is the finalizer for the FinaliserObject, which will be called 10,000 times, and can be called independently of whatever's going on on the static collection. I.E. yes, those objects can be destroyed while other objects are being dequeued from the collection. This isn't an issue.

静态集合本身不会被清理,直到应用程序本身将退出。

The static collection itself won't be cleaned up until the app itself exits.

请记住,还有的绝对的无法保证何时或是否之前,应用程序本身将退出那些终结将被调用。你的静态集合可能是当你退出完全是空的。

Keep in mind that there's absolutely no guarantee when or if those finalizers will be called before the app itself exits. Your static collection could be completely empty when you exit.

更糟的是,你要指定 itemValueOut ,无论你拉出队列的最后一个值是......这不是在项目的数量,因为你暗示在你的WriteLine()。由于这些析构函数被调用任何可能的顺序,理论上可以添加到队列10,000 9,999,9,998,... 2,1,按照这个顺序。

Worse, you're assigning itemValueOut to whatever the last value you pull out of the queue is... which is NOT the number of items created, as you imply in your WriteLine(). Because those destructors are called in any possible order, you could theoretically add to the queue 10,000, 9,999, 9,998, ... 2, 1, in that order.

这进一步是一个问题,因为你从队列中取出的10000倍,但在最后一个循环,它很可能不会有一个对象来消除,在这种情况下,你保证得到-1为项目数返回(即使其它9999项成功工作)。

Which is further an issue, because you're removing from the queue 10,000 times, but on the last loop, it's very possible there won't be an object to dequeue, in which case you're guaranteed to get -1 for the number of items returned (even if the other 9,999 items worked successfully).

要回答你的问题,这code不能死锁。死锁会发生什么,如果的AddItem()名为 TryGetItem(),但这些锁是pretty的多少保证让对方出的静态集合的同时,添加或删除项目。

To answer your question, this code cannot deadlock. A deadlock would happen if AddItem() called TryGetItem(), but those locks are pretty much guaranteed to keep each other out of the static collection while adding or removing items.

如果你是碰运气的是,你可以退出你的应用程序没有所有的 FinaliserObject 取值已增加自己的队列。含义终结的一个可以发射并尝试添加到 FinaliserCollection ,而 FinaliserCollection 已被释放。你在做什么,在finaliser是可怕

Where you're tempting fate is that you can exit your app without all of the FinaliserObjects having added themselves to the queue. Meaning one of the finalizers could fire and try to add to the FinaliserCollection, but the FinaliserCollection has already been disposed. What you're doing in the finaliser is terrible.

但是,是的,终结呼叫可你调用而发生 FinaliserCollection.TryGetItem()。终结将阻塞等待,直到 TryGetItem()出现从锁(),在这一点上,将再增加项目。这不是一个问题。

But yes, a finalizer call can happen while you're calling FinaliserCollection.TryGetItem(). The finalizer will block and wait until TryGetItem() emerges from the lock(), at which point it will add another item. This is not an issue.

对于睡眠()命令,你可能只是扔垃圾收集的时间了。请记住,你的对象将不会收集/敲定,直到GC决定它需要的资源。

As for the sleep() command, you're probably just throwing the timing of the garbage collection off. Remember, your objects won't be collected/finalized until the GC decides it needs the resources.

抱歉这么强调......我知道你只是想测试一个概念,但我真的不明白你为什么会想这样做你想要做的,终结了什么。如果真的是一个合法的目标在这里,做它的终结是的没有的正确答案。

Sorry for being so emphatic... I know you're just trying to test a concept but I really don't understand why you would want to do what you're trying to do in the finalizer. If there's really a legitimate goal here, doing it in the finalizer is not the correct answer.

修改 从什么我读什么萨沙是说,不,你不会有一个僵局。终结器线程可能被阻止等待锁,但是GC将不会等待终结,因此将取消挂起线程,使锁被释放。

Edit From what I'm reading and what Sasha is saying, no you will not have a deadlock. The finalizer thread may be blocked waiting for the lock, but the GC will not wait for the finalizer, and will thus unsuspend the threads, allowing the locks to be released.

在任何情况下,这就是为什么你不应该拨打电话这样的一个终结一个非常有力的论据......终结是的只有的释放非托管资源。还有什么是玩轮盘赌。

In any case, this is a very strong argument for why you shouldn't be making calls like this in a finalizer... the finalizer is only for releasing unmanaged resources. Anything else is playing roulette.