使用 Monitor.Wait 和 Monitor.Pulse 时出现内存不足异常

Out of memory exception while playing with Monitor.Wait and Monitor.Pulse

我在 .NET 中使用 Monitor class,所以我得到了一段代码,它似乎可以正常工作,但是当我循环它一段时间时,它会抛出一个 OutOfMemoryException

我 运行 在 64 位 windows 8 位开发人员机器上使用 8 GB RAM,我的进程从未占用超过 100 MB space内存。

这是我的代码:

    using System;
    using System.Threading;

    public class Program
    {
        public static void Main()
        {
            while (true) {

                object theLock = new Object();
                Thread threadA = new Thread(() =>
                {
                    Console.WriteLine("Thread A before lock");
                    lock (theLock)
                    {
                        Console.WriteLine("Thread A locking, about to Wait");
                        Monitor.Wait(theLock);
                    }
                    Console.WriteLine("Thread A after lock");
                });

                Thread threadB = new Thread(() =>
                {
                    Console.WriteLine("Thread B before lock");
                    lock (theLock)
                    {
                        Console.WriteLine("Thread B lockint, about to Pulse");
                        Monitor.Pulse(theLock);
                    }
                    Console.WriteLine("Thread B before lock");
                });

                threadA.Start();
                threadB.Start();

                GC.Collect();

            }
        }
    }

我读到 here 这可能是一个碎片问题,我在最后添加了 GC.Collect()。但是我没有分配大块的 space.

然后我决定测量循环在抛出异常之前大约经历了多少次迭代并添加了一个计数器:

    using System;
    using System.Threading;

    public class Program
    {
        public static void Main()
        {
            int counter = 0;

            while (true) {
                Console.WriteLine(counter);
                counter++;

                object theLock = new Object();
                Thread threadA = new Thread(() =>
                {
                    Console.WriteLine("Thread A before lock");
                    lock (theLock)
                    {
                        Console.WriteLine("Thread A locking, about to Wait");
                        Monitor.Wait(theLock);
                    }
                    Console.WriteLine("Thread A after lock");
                });

                Thread threadB = new Thread(() =>
                {
                    Console.WriteLine("Thread B before lock");
                    lock (theLock)
                    {
                        Console.WriteLine("Thread B lockint, about to Pulse");
                        Monitor.Pulse(theLock);
                    }
                    Console.WriteLine("Thread B before lock");
                });

                threadA.Start();
                threadB.Start();

                GC.Collect();

            }
        }
    }

这似乎大大减慢了异常的抛出速度。我测量了 36000 次迭代。

对于每对线程,如果线程 A 设法在线程 B 之前获取锁,您将以两个线程都完成而告终,并且可以清除所有内容。

如果线程 B 设法在线程 A 之前获取锁,则线程 B 将完成(已对监视器发出脉冲)但随后线程 A 将获取监视器并等待 永远 某事脉冲它。所以到那时你将拥有:

  • 一个Thread对象
  • 一个OS线程
  • 线程正在等待的对象

...基本上所有这些都永远捆绑在一起。

鉴于此,我对您遇到问题并不感到惊讶。

不清楚您要达到什么目的,但这可以解释症状。永远不要假设仅仅因为你在 threadB.Start() 之前调用 threadA.Start(),第一个线程实际上会在第二个线程之前到达代码中的某个点。