title: How to synchronize a Producer thread and a Consumer thread [C++/CLI]
body: <p><em>EDIT</em>: Forget to say the program has to be in .NET 3.5; Visual Studio 2008 and Windows 7.</p>
  
  <p>In my old MSDN library (for Visual Studio 2008) documentation, I come across an article about threading in C#. I try to convert to C++/CLI. I have encountered many problems in converting. Now there is only one left: <code>C3371: Cannot take the address of 'Producer::ThreadRun' unless creating delegate instance</code>. </p>
  
  <p>Title of the original doc: <em>How to: Synchronize a Producer and a Consumer Thread (C# Programming Guide)</em></p>
  
  <p>I cannot find the original document in online MSDN library. It seems like it has been deleted. Nor I cannot find it anywhere on the web.</p>
  
  <p>In the document, the code has:</p>
  
  <ul>
  <li>A producer thread that keeps 20 integers in the queue</li>
  <li>A consumer thread that works on the number just input in the queue</li>
  <li>A main thread will display number in queue every 2.5 seconds. As the main thread works on the queue, other threads are stopped. </li>
  </ul>
  
  <p>Well, here is the original code in C#:</p>
  
  <pre><code>using System;
  using System.Threading;
  using System.Collections;
  using System.Collections.Generic;
  
  public class SyncEvents
  {
      public SyncEvents()
      {
  
          _newItemEvent = new AutoResetEvent(false);
          _exitThreadEvent = new ManualResetEvent(false);
          _eventArray = new WaitHandle[2];
          _eventArray[0] = _newItemEvent;
          _eventArray[1] = _exitThreadEvent;
      }
  
      public EventWaitHandle ExitThreadEvent
      {
          get { return _exitThreadEvent; }
      }
      public EventWaitHandle NewItemEvent
      {
          get { return _newItemEvent; }
      }
      public WaitHandle[] EventArray
      {
          get { return _eventArray; }
      }
  
      private EventWaitHandle _newItemEvent;
      private EventWaitHandle _exitThreadEvent;
      private WaitHandle[] _eventArray;
  }
  public class Producer
  {
      public Producer(Queue&lt;int&gt; q, SyncEvents e)
      {
          _queue = q;
          _syncEvents = e;
      }
      // Producer.ThreadRun
      public void ThreadRun()
      {
          int count = 0;
          Random r = new Random();
          while (!_syncEvents.ExitThreadEvent.WaitOne(0, false))
          {
              lock (((ICollection)_queue).SyncRoot)
              {
                  while (_queue.Count &lt; 20)
                  {
                      _queue.Enqueue(r.Next(0, 100));
                      _syncEvents.NewItemEvent.Set();
                      count++;
                  }
              }
          }
          Console.WriteLine("Producer thread: produced {0} items", count);
      }
      private Queue&lt;int&gt; _queue;
      private SyncEvents _syncEvents;
  }
  
  public class Consumer
  {
      public Consumer(Queue&lt;int&gt; q, SyncEvents e)
      {
          _queue = q;
          _syncEvents = e;
      }
      // Consumer.ThreadRun
      public void ThreadRun()
      {
          int count = 0;
          while (WaitHandle.WaitAny(_syncEvents.EventArray) != 1)
          {
              lock (((ICollection)_queue).SyncRoot)
              {
                  int item = _queue.Dequeue();
              }
              count++;
          }
          Console.WriteLine("Consumer Thread: consumed {0} items", count);
      }
      private Queue&lt;int&gt; _queue;
      private SyncEvents _syncEvents;
  }
  
  public class ThreadSyncSample
  {
      private static void ShowQueueContents(Queue&lt;int&gt; q)
      {
          lock (((ICollection)q).SyncRoot)
          {
              foreach (int item in q)
              {
                  Console.Write("{0, 2} ", item);
              }
          }
          Console.WriteLine();
      }
  
      static void Main()
      {
          Queue&lt;int&gt; queue = new Queue&lt;int&gt;();
          SyncEvents syncEvents = new SyncEvents();
  
          Console.WriteLine("Configuring worker threads...");
          Producer producer = new Producer(queue, syncEvents);
          Consumer consumer = new Consumer(queue, syncEvents);
          Thread producerThread = new Thread(producer.ThreadRun);
          Thread consumerThread = new Thread(consumer.ThreadRun);
  
          Console.WriteLine("Launching producer and consumer threads...");
          producerThread.Start();
          consumerThread.Start();
  
          for (int i = 0; i &lt; 4; i++)
          {
              Thread.Sleep(2500);
              ShowQueueContents(queue);
          }
  
          Console.WriteLine("Signaling threads to terminate...");
          syncEvents.ExitThreadEvent.Set();
  
          producerThread.Join();
          consumerThread.Join();
      }
  }
  </code></pre>
  
  <p>I have converted to the following C++/CLI. Please note that </p>
  
  <ul>
  <li>There is no getter as I find out if I have getter I cannot get the WaitOne function in <code>Producer::ThreadRun</code></li>
  <li>Instead of <code>lock</code> in C#, I use <code>Monitor::Enter</code> and <code>Monitor::Exit</code></li>
  <li>Compilation error in <code>main()</code> when creating a thread at <code>gcnew Thread(&amp;Producer::ThreadRun)</code></li>
  <li>Error: C3374: Cannot take the address of <code>Producer::ThreadRun</code> unless creating delegate instance.</li>
  </ul>
  
  <p>code in C++/CLI:</p>
  
  <pre><code>using namespace System;
  using namespace System::Threading;
  using namespace System::Collections;
  using namespace System::Collections::Generic;
  
  public ref class SyncEvents
  {
  public:
      property System::Threading::EventWaitHandle ^ _newItemEvent;
      property System::Threading::EventWaitHandle ^ _exitThreadEvent;
      property array&lt;System::Threading::WaitHandle ^&gt; ^ _eventArray;
  
  public:
      SyncEvents()
      {
          _newItemEvent = gcnew System::Threading::AutoResetEvent(false);
          _exitThreadEvent = gcnew System::Threading::ManualResetEvent(false);
  
          _eventArray = gcnew array&lt;System::Threading::WaitHandle ^&gt;(2);
          _eventArray[0] = _newItemEvent;
          _eventArray[1] = _exitThreadEvent;
      }
  
  };
  
  public ref class Producer
  {
  private:
      System::Collections::Generic::Queue&lt;int&gt; ^ _queue;
      SyncEvents ^ _syncEvents;
  
  public:
      Producer(System::Collections::Generic::Queue&lt;int&gt; ^ q, SyncEvents ^ e)
      {
          _queue = q;
          _syncEvents = e;
      }
      void ThreadRun()
      {
          int count = 0;
          Random ^ r = gcnew Random();
  
          while (!_syncEvents-&gt;_exitThreadEvent-&gt;WaitOne(0, false))
          {
              Monitor::Enter(((System::Collections::ICollection ^)_queue)-&gt;SyncRoot);
              try
              {
                  while (_queue-&gt;Count &lt; 20)
                  {
                      _queue-&gt;Enqueue(r-&gt;Next(0, 100));
                      _syncEvents-&gt;_newItemEvent-&gt;Set();
                      count++;
                  }
              }
              finally
              {
                  Monitor::Exit(((System::Collections::ICollection ^)_queue)-&gt;SyncRoot);
              }
          }
          Console::WriteLine("Producer thread: produced {0} items", count);
      }
  };
  
  public ref class Consumer
  {
  private:
      System::Collections::Generic::Queue&lt;int&gt; ^ _queue;
      SyncEvents ^ _syncEvents;
  
  public:
      Consumer(System::Collections::Generic::Queue&lt;int&gt; ^ q, SyncEvents ^ e)
      {
          _queue = q;
          _syncEvents = e;
      }
      void ThreadRun()
      {
          int count = 0;
  
          while (System::Threading::WaitHandle::WaitAny(_syncEvents-&gt;_eventArray) != 1)
          {
              Monitor::Enter(((System::Collections::ICollection ^)_queue)-&gt;SyncRoot);
              try
              {
                  int item = _queue-&gt;Dequeue();
              }
              finally
              {
                  Monitor::Exit(((System::Collections::ICollection ^)_queue)-&gt;SyncRoot);
              }
              count++;
          }
          Console::WriteLine("Consumer Thread: consumed {0} items", count);
      }
  };
  
  static void ShowQueueContents(System::Collections::Generic::Queue&lt;int&gt; ^ _q)
  {
      Monitor::Enter(((System::Collections::ICollection ^)_q)-&gt;SyncRoot);
      try
      {
          for each (int item in _q)
              Console::WriteLine("{0, 3} ", item);
      }
      finally
      {
          Monitor::Exit(((System::Collections::ICollection ^)_q)-&gt;SyncRoot);
      }
  };
  
  int main()
  {
      System::Collections::Generic::Queue&lt;int&gt; ^ queue 
          = gcnew System::Collections::Generic::Queue&lt;int&gt;(); 
      SyncEvents ^ syncEvents = gcnew SyncEvents();
  
      Console::WriteLine("Configuring worker threads...");
      Producer ^ producer = gcnew Producer(queue, syncEvents);
      Consumer ^ consumer = gcnew Consumer(queue, syncEvents);
      System::Threading::Thread ^ producerThread = gcnew System::Threading::Thread(&amp;Producer::ThreadRun);
      System::Threading::Thread ^ consumerThread = gcnew System::Threading::Thread(&amp;Consumer::ThreadRun);
  
      Console::WriteLine("Launching producer and cosumer threads...");
      producerThread-&gt;Start();
      consumerThread-&gt;Start();
  
      for (int i = 0; i &lt; 4; i++)
      {
          Thread::Sleep(2500);
          ShoqQueueuContents(queue);
      }
  
      Console::WriteLine("Signaling threads to terminate...");        
      syncEvents-&gt;_exitThreadEvent-&gt;Set();
  
      producerThread-&gt;Join();
      consumerThread-&gt;Join();
  }
  </code></pre>