生产者消费者疑问

nowordwind 2008-12-16 10:47:17
想练下生产者消费者。写了一个小例子,但是运行时有问题。如果线程这样的次序创建:先建所有生产者线程,再建所有消费者线程,则是对的。但是如果把生产者与消费者线程轮着建立,则运行结果不对。

主要代码如下:


#pragma once

#include <windows.h>

typedef struct node
{
int iId;
char name[32];
}NODE, *PNODE;



class CQueue
{
CQueue(const CQueue& rhs);
public:
CQueue(int nCapity = 10);
~CQueue();
void Lock(bool bLock = TRUE);
bool Insert(PNODE pNode);
void Pop(PNODE pNode);
int iCapity;
int iHead;
int iTail;
int iCount;
CRITICAL_SECTION cs;
HGLOBAL gBuffer;
HANDLE hEvent;
HANDLE hPop, hInsert;
BYTE threadID;
};



#include "queue.h"
#include <stdio.h>
#include <windows.h>


CQueue::CQueue(int nCapity /* = 1000 */)
{
InitializeCriticalSection(&cs);
iCapity = nCapity;
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
hPop = CreateEvent(NULL, FALSE, FALSE, NULL);
hInsert = CreateEvent(NULL, FALSE, FALSE, NULL);
gBuffer = GlobalAlloc(GPTR, nCapity * sizeof(NODE));
threadID = -1;
if (NULL == gBuffer)
{
;
}
iHead = iTail = iCount = 0;
}

void CQueue::Lock(bool bLock /* = TRUE */)
{
if (bLock)
{
EnterCriticalSection(&cs);
}
else
{
LeaveCriticalSection(&cs);
}
}

CQueue::~CQueue()
{
DeleteCriticalSection(&cs);
CloseHandle(hEvent);
CloseHandle(hPop);
CloseHandle(hInsert);
if (NULL != gBuffer)
{
GlobalFree(gBuffer);
}
}

bool CQueue::Insert(PNODE pNode)
{
Lock(TRUE);
pNode->iId++;
if (iCount >= iCapity)
{
Lock(false);
WaitForSingleObject(hInsert, INFINITE);
Lock(true);
}

if (iCount==0)
{
iHead = 0;
}
int threadId = GetCurrentThreadId();
printf("IN[%d]:iId=%d\n", threadId, pNode->iId);
memcpy((LPVOID)((DWORD)gBuffer + iTail*sizeof(NODE)), pNode, sizeof(NODE));
iTail = (iTail+1) % iCapity;
iCount++;
SetEvent(hPop);
Lock(FALSE);

return true;
}


void CQueue::Pop(PNODE pNode)
{
if(NULL == pNode)
return;
Lock(TRUE);
if (iCount==0)
{
Lock(FALSE);
WaitForSingleObject(hPop, INFINITE);
Lock(TRUE);
}

LPVOID des = (LPVOID)((DWORD)gBuffer + iHead*sizeof(NODE));
memcpy((LPVOID)pNode, des, sizeof(NODE));
iHead = (iHead+1) % iCapity;
iCount--;
if (pNode != NULL)
{
int threadId = GetCurrentThreadId();
printf("OUT[%d]:iId=%d\n", threadId, pNode->iId);
}
SetEvent(hInsert);
Lock(FALSE);

}




以下为主程序单元中的

#include <iostream>
#include <conio.h>
#include <windows.h>

#include "queue.h"

using namespace std;

#define MAX_GET 10
#define MAX_INSERT 10

static NODE m_node;
static CQueue queue;

DWORD WINAPI Insert(LPVOID lParam)
{
CQueue* pQueue = (CQueue*)lParam;
if (pQueue == NULL)
{
return 0;
}

// while (WAIT_TIMEOUT == WaitForSingleObject(pQueue->hEvent, 0))
{
pQueue->Insert(&m_node);
Sleep(5000);
}
return 0;
}

DWORD WINAPI Pop(LPVOID lParam)
{
CQueue* pQueue = (CQueue*)lParam;
if (pQueue == NULL)
{
return 0;
}
NODE PopValue;
// while (WAIT_TIMEOUT == WaitForSingleObject(pQueue->hEvent, 0))
{
pQueue->Pop(&PopValue);
Sleep(5000);
}

return 0;
}

HANDLE threadGet[MAX_GET];
HANDLE threadInsert[MAX_INSERT];

int main(int argc, char* argv)
{


for (int num = 0; num < MAX_GET; num++)
{
threadGet[num] = CreateThread(NULL, 0, Insert, (LPVOID)&queue, 0, NULL);

CloseHandle(threadGet[num]);
}

for(num = 0; num < MAX_INSERT; num++)
{
threadInsert[num] = CreateThread(NULL, 0, Pop, (LPVOID)&queue, 0, NULL);
CloseHandle(threadInsert[num]);
}

cout<<"press any key to exit."<<endl;
while (!kbhit())
{

}
SetEvent(queue.hEvent);
WaitForMultipleObjects(MAX_GET, threadGet, true, INFINITE);
WaitForMultipleObjects(MAX_INSERT, threadInsert, true, INFINITE);
return 0;
}
...全文
131 5 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
IONPhantom 2009-01-22
  • 打赏
  • 举报
回复
你的问题出在 bool CQueue::Insert(PNODE pNode) 函数里

if(iCount==0)
{
iHead=0;
}
你仔细看看这里,iCount==0的时候你把iHead置0,在Pop线程中执行了iCount--;所以每当Pop一次之后再Insert的时候你都把iHead置0,程序可不就是你上面那个样子啦
hityct1 2009-01-17
  • 打赏
  • 举报
回复
也不标注一下,看起来麻烦。
nowordwind 2008-12-16
  • 打赏
  • 举报
回复
注明:线程创建的次序如果是先创建所有生产者,再在另一个循环中创建消费者,打印结果是这样的

press any key to exit.
IN[4508]:iId=1
IN[2296]:iId=2
IN[5568]:iId=3
IN[5868]:iId=4
IN[5972]:iId=5
IN[4240]:iId=6
IN[3316]:iId=7
IN[4044]:iId=8
IN[3408]:iId=9
IN[3400]:iId=10
OUT[4300]:iId=1
OUT[2712]:iId=2
OUT[5168]:iId=3
OUT[4116]:iId=4
OUT[3168]:iId=5
OUT[4448]:iId=6
OUT[3660]:iId=7
OUT[4452]:iId=8
OUT[1484]:iId=9
OUT[5100]:iId=10

但是我也不明白,为什么不是交替的,形如IN,再OUT,再IN之类的。这种次序也太特殊了。另外我在线程函数中以前是循环的。因为测试方便就把两个while都注释掉了。还有里面的sleep函数只是为了查看方便。似乎加了sleep后,对结果也有影响。以前代码为完整的。大家可以建个工程,添加后运行下。这个问题已经搞了一天了。
nowordwind 2008-12-16
  • 打赏
  • 举报
回复
改成如上混杂的创建方式后,打印出来的东西如下:

press any key to exit.
IN[2564]:iId=1
OUT[3284]:iId=1
IN[5596]:iId=2
OUT[3332]:iId=1
IN[1480]:iId=3
OUT[4128]:iId=1
IN[5392]:iId=4
OUT[3616]:iId=1
IN[5952]:iId=5
OUT[5780]:iId=1
IN[5548]:iId=6
OUT[3664]:iId=1
IN[5044]:iId=7
OUT[5888]:iId=1
IN[5136]:iId=8
OUT[4948]:iId=1
OUT[4336]:iId=2
IN[6140]:iId=9
IN[392]:iId=10
OUT[3520]:iId=1

发现每次弹出的数都是1,跟踪代码是发现每次iHead都是个0,并没有增加,iCount这个值每次也是个0
nowordwind 2008-12-16
  • 打赏
  • 举报
回复
以上在main中创建线程的次序改成如下,则出现问题,我没有用信号量,因为看网上有这种方案,遇到问题了,不清楚,所以想问明白点。

以下是会出错的创建次序:

for (int num = 0; num < MAX_GET; num++)
{
threadGet[num] = CreateThread(NULL, 0, Insert, (LPVOID)&queue, 0, NULL);
threadInsert[num] = CreateThread(NULL, 0, Pop, (LPVOID)&queue, 0, NULL);
CloseHandle(threadInsert[num]);
CloseHandle(threadGet[num]);
}

15,473

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧