MultiThreading生产者/消费者

时间:2016-09-23 22:00:59

标签: c++ multithreading winapi consumer producer

我正在编写基本的生产者/消费者线程代码。我得到了大部分工作,但我遇到了rand()的问题。或者也许我的问题比rand()更深,rand只是冰山一角。我不想做任何太复杂的事情(没有一个可运行或等待)。

我有一个充当缓冲区的整数全局双参数。我允许用户输入运行时间的大小和限制。 我使计数器成为一个静态全局变量。 这是我的制片人:

DWORD WINAPI producer(LPVOID n)
{
  cout << "\nPRODUCER:The producer is running right now" << endl;
  int size = (int)n;
  int num = rand()%10;// this is for making item.
  while (buffer.size() > size)
    {
    }

  buffer.push_back(num);
  counter++;
  return (DWORD)n;

}

这是我的消费者 -

 DWORD WINAPI consumer(LPVOID n)
 {

   cout << "\nCONSUMER:The consumer is running right now" << endl;
   while (buffer.empty())
     { }
   int item= buffer.front();
   cout << "\nCONSUMER:The consumer ate" << item << endl;
   counter++;

   return (DWORD)n;


  }

在主 -

 while (counter < l)
  {
     hThreads[0] = CreateThread(NULL, 0, producer, (LPVOID)s, NULL, &id[0]);
     hThreads[1] = CreateThread(NULL, 0, consumer, (LPVOID)l, NULL, &id[1]);

     waiter = WaitForMultipleObjects(MAX_THREADS, hThreads, TRUE, INFINITE);
   }
  for (int i = 0; i < MAX_THREADS; i++) {
     CloseHandle(hThreads[i]);
   }

我的输出是这样的: enter image description here

所以每次只生成1个。 Srand也没有工作。但它运行的次数正确。

EDIT --- 所以我修复了生产者和消费者来处理竞争条件:

  DWORD WINAPI producer(LPVOID s)
  {
     WaitForSingleObject(Empty, INFINITE);    
     WaitForSingleObject(Mutex, INFINITE);
     cout << "\nPRODUCER...." << endl;
     int size = (int)s;
     srand(size);
     int in = rand() % 10;
     cout << "THIS IS IN:::" << in << endl;
     while (buffer.size() == size)
     {
        ReleaseMutex(Mutex);
      }
     buffer.push_back(in);
     counter++;
     cout << "\nThe producer produces " << buffer.front() << endl;
     ReleaseMutex(Mutex);
     ReleaseSemaphore(Full, 1, NULL);

     return (DWORD)s;
     }



     DWORD WINAPI consumer(LPVOID l)
     {
        WaitForSingleObject(Full, INFINITE);   
        WaitForSingleObject(Mutex, INFINITE);
        cout << "\nCONSUMER...." << endl;
        while (buffer.empty())
       {
             ReleaseMutex(Mutex);

       }
    int out = buffer.front();
    counter++;
    ReleaseMutex(Mutex);
    ReleaseSemaphore(Empty, 1, NULL);
    return (DWORD)l;
    }

但是随机的事情仍然在继续。它只能反复生成一个数字(即使是播种时)。

1 个答案:

答案 0 :(得分:0)

是的,创建(并销毁)一个线程只创建或处理一个数字是没有意义的 - 额外的开销是不值得的。加上你的代码(按原样)有一些非常明显的错误或误解。这些是:

  • 在主线程中你创建了工作线程(在那个while(){}循环中)但是只在最后销毁它们一次,也就是说你只销毁在最后一个循环中创建的句柄。
  • 如我的消息中所述,为每个要生成的数字调用srand(),并且始终使用相同的初始种子,因此获得相同的数字是正常的。
  • while()循环检查缓冲区是空的还是完全没有意义,并且不应该释放Mutex。
  • counter变量的操作可能是错误的。生产者和消费者线程都增加了它,主线程使用它来确定生成/打印的数量。
  • 在您的初始代码片段中,counter是一个全局变量,在其上运行多个线程,因此您应该以线程安全的方式读取或修改它,而不是这种方式。您应该使用一些锁定机制,如关键部分或互锁变量访问。

我的建议是创建一个生产者线程(生成所有数字)和一个消费者线程(打印所有数字),通过缓冲区传输。为此,您需要以下项目(您已经实施了大部分项目):

  • A&#34; Full&#34;信号量,计算缓冲区中的数字,最初为0。
  • 补充&#34;空&#34;信号量,计算缓冲区中的空项,最初设置为缓冲区大小。 &#34;总和&#34;这些信号量当然应该总是等于缓冲区大小。
  • 用于访问缓冲区的互斥锁(或关键部分)。
  • 一个全局变量,用于告诉线程是否退出。

我在下面发布了一些代码示例。不确定它们是否有效,您可能需要修改或调试它们,但这只是为了展示这个概念:

// Global
#define MAX_BUF 5
BOOL bExit = FALSE;

// Main Thread
    Empty = CreateSemaphore(NULL, MAX_BUF, MAX_BUF, NULL);
    Full = CreateSemaphore(NULL, 0, MAX_BUF, NULL);
    .
    .
    hThreads[0] = CreateThread(NULL, 0, producer, (LPVOID)l, NULL, &id[0]);
    hThreads[1] = CreateThread(NULL, 0, consumer, (LPVOID)l, NULL, &id[1]);
    waiter = WaitForMultipleObjects(MAX_THREADS, hThreads, TRUE, INFINITE);

    for (int i = 0; i < MAX_THREADS; i++)
        CloseHandle(hThreads[i]);


DWORD WINAPI producer(LPVOID nCount)
{
    int nItems = (int)nCount;
    // Initialize rand() seed - each run will be generating a different sequence
    srand(GetTickCount()); // May need to AND GetTickCount() with RAND_MAX ???
    // Generate nCount numbers
    for (int i = 0; i < nItems; i++)
    {
        if (bExit) return 9; // Aborted
        WaitForSingleObject(Empty, INFINITE); // Wait until at least one item empty
        // Lock the buffer and add an item
        WaitForSingleObject(Mutex, INFINITE); // Could be EnterCriticalSection() instead
        if (buffer.size() >= MAX_BUF)
        {
            cout << "\nInternal Error: Buffer-full Check Failed!" << endl;
            bExit = TRUE; // Tell all threads to exit
            ReleaseMutex(Mutex); 
            return 1; // Exit with Error
        }
        int in = rand() % 10;
        buffer.push_back(in);
        cout << "The Producer generated: " << in << endl;
        ReleaseMutex(Mutex); // Could be LeaveCriticalSection() instead
        ReleaseSemaphore(Full, 1, NULL); // 1 item added, update semaphore
    }
    cout << "\nThe PRODUCER produced " << nItems << " items." << endl;
    return 0; // OK
}

DWORD WINAPI consumer(LPVOID nCount)
{
    int nItems = (int)nCount;
    // Generate nCount numbers
    for (int i = 0; i < nItems; i++)
    {
        if (bExit) return 9; // Aborted
        WaitForSingleObject(Full, INFINITE); // Wait until at least one item in buffer
        // Lock the buffer and get an item
        WaitForSingleObject(Mutex, INFINITE); // Could be EnterCriticalSection() instead
        if (buffer.empty())
        {
            cout << "\nInternal Error: Buffer-empty Check Failed!" << endl;
            bExit = TRUE; // Tell all threads to exit
            ReleaseMutex(Mutex); 
            return 2; // Exit with Error
        }
        int out = buffer.front();
        buffer.erase(buffer.begin()); // Remove item from the list
        cout << "The Consumer ate: " << out << endl;
        ReleaseMutex(Mutex);  // Could be LeaveCriticalSection() instead
        ReleaseSemaphore(Empty, 1, NULL); // 1 item removed, update semaphore
    }
    cout << "\nThe CONSUMER consumed " << nItems << " items." << endl;
    return 0; // OK
}

注意:

  • bExit变量是全局的,由多个线程访问/修改,但由于这总是在关键部分(互斥锁)内完成,因此不需要使用另一个或互锁变量访问
  • 诊断消息(例如cout << "The Consumer ate: " << out << endl;)或任何其他类型的&#34;处理&#34;在释放对象之后,这些数据(或者#34;工作&#34;)可以放置。这将更有效,将对象更早地释放到其他线程。我已经这样做了,以便更好地说明测试中的事件顺序。
  • 如果您将MAX_BUF设置为1,则应一次生成/打印一个数字,否则无法确定,但产生的项目减去消耗的项目当然不应超过缓冲区大小。