Logger code, please review

Here is a thread safe logger without the monitor2 or blocking queue. It manually does the same thing in code without the other classes:

using System;
using System.Collections;
using System.IO;
using System.Threading;
using Microsoft.SPOT;

namespace MyApp
{
    public class Logger : IDisposable
    {
        StreamWriter sw;
        Queue q;
        AutoResetEvent wait = new AutoResetEvent(false);
        bool isDisposed;

        public Logger(string path)
        {
            if (path != null) // Allow testing without a file.
                sw = new StreamWriter(path, true);
            q = new Queue();
            new Thread(ReaderLoop).Start();
        }

        public void Write(string msg)
        {
            if (msg == null) throw new ArgumentNullException("msg");
            lock (q)
            {
                ThrowIfDisposed();
                q.Enqueue(msg);
                wait.Set();
            }
            Debug.Print("Logger wrote: " + msg);
        }

        public void Close()
        {
            lock (q)
            {
                if (isDisposed) return;
                this.isDisposed = true;
                q.Enqueue(null);
                wait.Set(); // signal reader if waiting.
                if (sw != null)
                    sw.Close();
            }
        }

        public void Dispose()
        {
            this.Close();
        }

        private void ThrowIfDisposed()
        {
            if (this.isDisposed)
                throw new InvalidOperationException("Disposed");
        }

        private void ReaderLoop()
        {
            while (!isDisposed)
            {
                string s = Read();
                if (s == null) break;
                if (this.sw!=null)
                    sw.Write(s);
                else
                    Debug.Print("Logger wrote: " + s);
            }
            Debug.Print("Logger Reader thread exited.");
        }

        private string Read()
        {
            Monitor.Enter(q);
            try
            {
                if (isDisposed) return null;
                while (q.Count == 0)
                {
                    Wait(); // Wait for producer.
                }
                string msg = q.Dequeue() as string;
                return msg;
            }
            finally
            {
                Monitor.Exit(q);
            }
        }

        private bool Wait(int millisecondsTimeout=-1, bool exitContext = true)
        {
            // Exit lock before wait. Need to own it to exit it.
            Monitor.Exit(q);
            try
            {
                // If we timeout waiting for Pulse, return false.
                return wait.WaitOne(millisecondsTimeout, exitContext);
            }
            finally
            {
                // Always re-enter the lock after wait.
                Monitor.Enter(q);
            }
        }
    }
}