有没有办法阻止ThreadPool制作垃圾?

时间:2016-01-15 14:23:39

标签: c# unity3d

执行多线程方法会产生垃圾。为什么会这样,我们可以阻止它吗?

ThreadPool.QueueUserWorkItem(callBack, state);

enter image description here

修改 垃圾我指的是创建的对象然后超出了范围。垃圾收集非常慢,因为它的旧版单声道。所以你从GC中保存的每一个kb都是一个胜利。如果您不熟悉统一引擎,请在屏幕截图中查看突出显示行上的GC列。它说0.6kb。因此它创建了600字节的垃圾。回调代码没有创建任何垃圾,所以这是根据ThreadPool.QueueUserWorkItem

编辑2:这里进一步详细说明是一个更具体的例子:

public class TestThread : MonoBehaviour
{
    public void Update()
    {
        if (Time.frameCount%10 == 0)
            ThreadPool.QueueUserWorkItem(DummyMethod);
    }

    public void DummyMethod(object meaningless)
    {
    }
}

结果如下。请查看突出显示的行。 GC栏显示285Bytes。由于DummyMethod没有做任何事情,垃圾与ThreadPool有关。

enter image description here

编辑3: 为了放松这种情况并找到替代方案,可以使用从队列中执行作业的工作线程。

没关系但是如果有多个CPU可用,那么必须运行在CPU以外的CPU上。 Unity几乎可以在一个线程中执行任何操作,因此同一CPU上的后台工作人员将成为灾难。它也是一个跨平台项目,因此仅限Windows的解决方案将无法运行。所以基本上我需要一个工作线程解决方案,并知道是否可以实现线程的CPU是否与另一个线程相同。

1 个答案:

答案 0 :(得分:5)

当你ThreadPool.QueueUserWorkItem(DummyMethod);实际隐含地将你的代码转换为ThreadPool.QueueUserWorkItem(new WaitCallback(DummyMethod));时,该回调可能是被放到GC上的项目。尝试以下代码以显式创建委托并保留对它的引用,并查看它是否减少了GCable数据的数量。

public class TestThread : MonoBehaviour
{
    private readonly WaitCallback _callback;

    public TestThread()
    {
        _callback = new WaitCallback(DummyMethod);
    }

    public void Update()
    {
        if (Time.frameCount%10 == 0)
            ThreadPool.QueueUserWorkItem(_callback);
    }

    public void DummyMethod(object meaningless)
    {
    }
}

更新:以下是单线程后台工作程序的一个非常基本的实现,为您提供一个起点。下面的代码是未经测试的,可能会表现得非常糟糕,但它确实会给你一个想法作为起点。

public class BasicBackgroundWorker
{
    private readonly Thread _backgroundWorkThread;
    private readonly Queue<Action> _queue = new Queue<Action>();
    private readonly ManualResetEvent _workAvailable = new ManualResetEvent(false);

    public BasicBackgroundWorker()
    {
        _backgroundWorkThread = new Thread(BackgroundThread)
        {
            IsBackground = true,
            Priority = ThreadPriority.BelowNormal,
            Name = "BasicBackgroundWorker Thread"
        };
        _backgroundWorkThread.Start();
    }

    public void EnqueueWork(Action work)
    {
        lock (_queue)
        {
            _queue.Enqueue(work);
            _workAvailable.Set();
        }
    }

    private void BackgroundThread()
    {
        while (true)
        {
            _workAvailable.WaitOne();
            Action workItem;
            lock (_queue)
            {
                workItem = _queue.Dequeue();
                if (_queue.Count == 0)
                {
                    _workAvailable.Reset();
                }
            }
            try
            {
                workItem();
            }
            catch (Exception ex)
            {
                //Log exception that happened in backgroundWork
            }
        }
    }
}