如何同步Producer线程和Consumer线程[C ++ / CLI]

时间:2014-08-06 09:47:43

标签: c# .net multithreading c++-cli

编辑:忘了说程序必须在.NET 3.5中; Visual Studio 2008和Windows 7。

在我的旧MSDN库(针对Visual Studio 2008)文档中,我遇到了一篇关于C#中的线程的文章。我尝试转换为C ++ / CLI。我在转换中遇到了很多问题。现在只剩下一个:C3371: Cannot take the address of 'Producer::ThreadRun' unless creating delegate instance

原始文档的标题:如何:同步生产者和消费者线程(C#编程指南)

我在在线MSDN库中找不到原始文档。好像它已被删除。我也无法在网络上找到它。

在文档中,代码包含:

  • 在队列中保留20个整数的生产者线程
  • 消费者线程,用于处理队列中输入的数字
  • 主线程每隔2.5秒会在队列中显示一个数字。当主线程在队列上工作时,其他线程将停止。

嗯,这是C#中的原始代码:

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<int> 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 < 20)
                {
                    _queue.Enqueue(r.Next(0, 100));
                    _syncEvents.NewItemEvent.Set();
                    count++;
                }
            }
        }
        Console.WriteLine("Producer thread: produced {0} items", count);
    }
    private Queue<int> _queue;
    private SyncEvents _syncEvents;
}

public class Consumer
{
    public Consumer(Queue<int> 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<int> _queue;
    private SyncEvents _syncEvents;
}

public class ThreadSyncSample
{
    private static void ShowQueueContents(Queue<int> q)
    {
        lock (((ICollection)q).SyncRoot)
        {
            foreach (int item in q)
            {
                Console.Write("{0, 2} ", item);
            }
        }
        Console.WriteLine();
    }

    static void Main()
    {
        Queue<int> queue = new Queue<int>();
        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 < 4; i++)
        {
            Thread.Sleep(2500);
            ShowQueueContents(queue);
        }

        Console.WriteLine("Signaling threads to terminate...");
        syncEvents.ExitThreadEvent.Set();

        producerThread.Join();
        consumerThread.Join();
    }
}

我已转换为以下C ++ / CLI。请注意

  • 没有吸气剂,因为我发现我是否有吸气剂我无法在Producer::ThreadRun中获得WaitOne功能
  • 我使用lockMonitor::Enter
  • 而不是C#中的Monitor::Exit
  • main()
  • 创建主题时gcnew Thread(&Producer::ThreadRun)中的编译错误
  • 错误:C3374:除非创建委托实例,否则无法取Producer::ThreadRun的地址。

C ++ / CLI中的代码:

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<System::Threading::WaitHandle ^> ^ _eventArray;

public:
    SyncEvents()
    {
        _newItemEvent = gcnew System::Threading::AutoResetEvent(false);
        _exitThreadEvent = gcnew System::Threading::ManualResetEvent(false);

        _eventArray = gcnew array<System::Threading::WaitHandle ^>(2);
        _eventArray[0] = _newItemEvent;
        _eventArray[1] = _exitThreadEvent;
    }

};

public ref class Producer
{
private:
    System::Collections::Generic::Queue<int> ^ _queue;
    SyncEvents ^ _syncEvents;

public:
    Producer(System::Collections::Generic::Queue<int> ^ q, SyncEvents ^ e)
    {
        _queue = q;
        _syncEvents = e;
    }
    void ThreadRun()
    {
        int count = 0;
        Random ^ r = gcnew Random();

        while (!_syncEvents->_exitThreadEvent->WaitOne(0, false))
        {
            Monitor::Enter(((System::Collections::ICollection ^)_queue)->SyncRoot);
            try
            {
                while (_queue->Count < 20)
                {
                    _queue->Enqueue(r->Next(0, 100));
                    _syncEvents->_newItemEvent->Set();
                    count++;
                }
            }
            finally
            {
                Monitor::Exit(((System::Collections::ICollection ^)_queue)->SyncRoot);
            }
        }
        Console::WriteLine("Producer thread: produced {0} items", count);
    }
};

public ref class Consumer
{
private:
    System::Collections::Generic::Queue<int> ^ _queue;
    SyncEvents ^ _syncEvents;

public:
    Consumer(System::Collections::Generic::Queue<int> ^ q, SyncEvents ^ e)
    {
        _queue = q;
        _syncEvents = e;
    }
    void ThreadRun()
    {
        int count = 0;

        while (System::Threading::WaitHandle::WaitAny(_syncEvents->_eventArray) != 1)
        {
            Monitor::Enter(((System::Collections::ICollection ^)_queue)->SyncRoot);
            try
            {
                int item = _queue->Dequeue();
            }
            finally
            {
                Monitor::Exit(((System::Collections::ICollection ^)_queue)->SyncRoot);
            }
            count++;
        }
        Console::WriteLine("Consumer Thread: consumed {0} items", count);
    }
};

static void ShowQueueContents(System::Collections::Generic::Queue<int> ^ _q)
{
    Monitor::Enter(((System::Collections::ICollection ^)_q)->SyncRoot);
    try
    {
        for each (int item in _q)
            Console::WriteLine("{0, 3} ", item);
    }
    finally
    {
        Monitor::Exit(((System::Collections::ICollection ^)_q)->SyncRoot);
    }
};

int main()
{
    System::Collections::Generic::Queue<int> ^ queue 
        = gcnew System::Collections::Generic::Queue<int>(); 
    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(&Producer::ThreadRun);
    System::Threading::Thread ^ consumerThread = gcnew System::Threading::Thread(&Consumer::ThreadRun);

    Console::WriteLine("Launching producer and cosumer threads...");
    producerThread->Start();
    consumerThread->Start();

    for (int i = 0; i < 4; i++)
    {
        Thread::Sleep(2500);
        ShoqQueueuContents(queue);
    }

    Console::WriteLine("Signaling threads to terminate...");        
    syncEvents->_exitThreadEvent->Set();

    producerThread->Join();
    consumerThread->Join();
}

1 个答案:

答案 0 :(得分:3)

 System::Threading::Thread ^ producerThread = 
      gcnew System::Threading::Thread(&Producer::ThreadRun);

Thread类构造函数需要委托对象。您习惯于自动将方法转换为委托的C#语言语法糖。但是在C ++ / CLI中不存在这种糖,你必须自己创建委托对象。并且要明确目标对象和目标方法,C#编译器想要推断目标对象。这确实为C ++ / CLI语言提供了一些C#所没有的功能,代价是必须编写更强大的代码:

 System::Threading::Thread ^ producerThread = 
      gcnew System::Threading::Thread(
          gcnew System::Threading::ThreadStart(producer, &Producer::ThreadRun)
      );

请考虑.NET 4中针对这些场景的现有支持,例如BlockingCollection&lt;&gt;和ConcurrentQueue&lt;&gt;。