1、目录多道程序缓冲区协调操作11.概述12使用的基本概念和原理22.1基本概念22.2 基本原理33总体设计34详细设计46测试时出现的问题及其解决方法257总结25多道程序缓冲区协调操作1.概述在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作。更多的情况是一些线程进行某些处理操作,而其它的线程必须对其处理结果进行了解。正常情况下对这种处理结果的了解应当在其处理任务完成后进行。如果不采取适当的措施,其它线程往往会在线程处理任务结束前就去访问处理结果,这就很有可能得到有关处理结果的错误的了解。例如,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线
2、程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。为了确保读线程读取到的是经过修改的变量,就必须在向变量写入数据时禁止其他线程对其的任何访问,直至赋值过程结束后再解除对其他线程的访问限制。像这种保证线程能了解其他线程任务处理结束后的处理结果而采取的保护措施即为线程同步。1.1设计目的(1)了解提出信号量的背景 (2)掌握信号量的基本概念及 PV 操作的原理 (3)进一步熟悉信号量解决的经典问题 (4)利用信号量的原理实现不同操作系统下同步互斥问题1.2设计要求(1) 可以随机产生字符数据,由put操作放入Buff1,buffer中容量单位是字符。
3、 (2) 提供良好图形界面,显示Buffer的操作过程。(3) 可以设定各Buffer的容量、PUT、GET、Move操作的个数;(4) 可以设定PUT、GET、Move操作的速度;(5) 实时显示每个Buffer中数据的个数和数据的内容,空闲Buffer的空间的个数;(6) 实时显示线程、进程所处于等待(阻塞)状态的个数(7)程序运行结束,显示汇总数据:总的运行时间;Buffer中数据的个数;已放入BUFFER的数据个数;已取出的数据个数;平均每个buffer中的数据个数。1.3主要完成的任务PutMove2Buffer1Buffer2Buffer3Get2Move1Get3图1对该模型(图
4、1)实例化,利用图形界面直观易懂的特点,把完全抽象的线程的就绪、阻止、运行的概念以及同步互斥的过程用图形动态的显示出来。1.4环境、工具 VC+ 6.0,WINDOW72使用的基本概念和原理2.1基本概念信号量:为解决多进程线程同步与互斥问题,让两个或多个多道进程线程通过特殊的变量展开交互。线程:是进程的一个实体,是进程上下文中执行的代码序列,是被系统调度的基本单元。进程:进程是正在运行的程序实体,并且包括这个运行的程序中占据的所有系统资源。互斥与同步:进程的同步与互斥是指进程在推进时的相互制约关系。进程同步:它主要源于进程合作,是进程间共同完成一项任务时直接发生相互作用的关系。进程互斥:它主
5、要源于资源共享,是进程之间的间接制约关系。2.2 基本原理本程序使用信号量作为同步互斥的工具,当然也可以用其它的,通过对BUFFER1、BUFFER2、BUFFER3的容量,还有控制权设置不同的信号量来协调PUT、GET2、GET3、MOVE1、MOVE2线程,使其同步来实现P、V操作。本程序并非真正的传输数据,只是对一个数据计数器加减来模拟数据的增加减少,被送来被取走,然后通过定时刷新,将线程的状态、数据显示到界面上。3总体设计3.1基本的技术路线MFC中已实现有对线程操作和同步互斥的全部类,但是他对实现P、V操作的模拟不够直观;使用API面向过程的话,比较灵活,但程序的可读性、维护性较差。
6、我们使用二者的优点,取长补短,采用混合编程的方式对问题进行描述和演示。3.2软件的总体结构、模块关系、总体流程模拟系统前台显示模块中间数模块后台线程模块图1 总体结构 显示模块数据层线程模块 设置 设置 显示 修改图2 模块关系参数设置开始暂停是否统计继续进行统计停止结束是否图3 流程图33线程规划我们创建三类线程:(1)PUT线程(往BUFFER1里放数据,相当于生产者)。(2)MOVE1线程(从BUFFER1里取数据并放到BUFFER2里,相当于搬运者)。 MOVE2线程(从BUFFER1里取数据并放到BUFFER3里,相当于搬运者)。(3)GET2线程(从BUFFER2里取数据,相当于消
7、费者)。GET3线程(从BUFFER3里取数据,相当于消费者)。每类线程可由用户自行设定线程的个数。4详细设计 4.1设计思想设计了3个主要函数,分别为:生产者函数put,消费者函数get2 、get3,转移函数move1 、move2,下面详细介绍各个类的功能。Put函数用于实现通过执行P操作判断buffer1是否有空,buffer1是否可操作,并向buffer1中置数据,同时将表示buffer1中数据数buffer1number加+1,放入数据后执行V操作。Move1函数通过执行P操作来判断buffer1中是否有数据,是否可操作,buffer2中是否有空间,是否可操作,将buffer1中的
8、数据移至buffer2中,并使buffer1number-,buffer2number+,确定buffer中现有的数据总数。之后再进行相应的V操作。Move2函数通过执行P操作来判断buffer1中是否有数据,是否可操作,buffer3中是否有空间,是否可操作,将buffer1中的数据移至buffer3中,并使buffer1number-,buffer3number+,确定buffer中现有的数据总数。之后再进行相应的V操作。Get2函数用于实现通过执行P操作判断buffer2是否有数据,buffer2是否可操作,并从buffer2中取数据,同时将表示buffer2中数据数buffer2num
9、ber-,放入数据后执行V操作。Get3函数用于实现通过执行P操作判断buffer3是否有数据,buffer3是否可操作,并从buffer3中取数据,同时将表示buffer3中数据数buffer3number-,放入数据后执行V操作。4.2设计原理生产者消费者问题是相互合作的进程关系的一种抽象,可以利用信号量机制来解决生产者消费者问题,利用互斥信号量mutex实现进程对缓冲池的互斥使用。对信号量的操作只能通过两个原子操作:Wait(s)和Signal(s).Wait(s)是等待信号的操作,进行S=S-1操作;Signal(s)是发送信号的操作,进行S=S+1操作。wait 若s-1后仍大于或等
10、于零,则进程继续执行;若s-1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度;若相加结果大于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度4.3信号量的设置需要设置九个信号量full1 empty1 buffer1 full2 empty2 buffer2 full3 empty3 buffer3。各信号量含义及初值如下:full1表示buffer1是否有数据,初值为0;empty1表示buffer1是否有空间,初值为m;buffer1表示buffer1是否可操作,初值为1;full2表示buffer2是否有数据,初值为0;empty
11、2表示buffer2是否有空间,初值为n;buffer2表示buffer2是否可操作,初值为1。full3表示buffer2是否有数据,初值为0;empty3表示buffer2是否有空间,初值为p;buffer3表示buffer2是否可操作,初值为1。4.4基本函数说明(1)线程创建函数HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes./线程安全属性DWORD dwStackSize,/线程堆栈的初始化大小,等于0时为系统默认的堆栈大小PLTHREAD_START_ROUTINE lpStartAddress,/线程函数L
12、PVOID lpParameter,/线程参数DWORD dwCreationFlags,/创建方式LPDWORD lpThreadId/线程标识符该函数创建一个线程,如果没有特殊需要除第三个参数外都可设为0,按系统的默认取值对待,主要是第三个参数,它是线程程序的入口,线程序入口的标准形式为UINT FUNCTION(LPVOID param)线程函数里可以传一个指针做为参数(param),该参数由创建线程函数(CreateThread)里的第四个参数确定。(2)信号量创建函数HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAtt
13、ributes, / 安全属性指针LONG lInitialCount, / 初始计数LONG lMaximumCount, / 最大计数LPCTSTR lpName / 对象名指针)该函数创建一个信号量,llnitialCount 是对信号量的初始值,iMaximunCount为信号量的最大值,即信号量在一个范围内有效,例empty=:CreateSemaphore(0,0,M,0);第二个0为empty信号量的初始值,M为信号量的最大值,在这个范围内(0,M)信号量都是有信号的。(3) 信号量释放函数BOOL ReleaseSemaphore(HANDLE hSemaphore, / 信号
14、量句柄LONG lReleaseCount, / 计数递增数量LPLONG lpPreviousCount / 先前计数)该函数为释放信号量,因为信号量为系统资源,数目有限,所以使用完毕后,最后将资源释放。(4)等待函数DWORD WaitForSingleObjec(HANDLE hHandle,/对象句柄DWORD dwMilliseconds/等待的时间,单位为毫秒)5.编码设计5.1开发环境的设置和建立打开VC+,点击文件新建项目,弹出新建项目对话框,选择MFCMFC应用程序,为我们的项目起一个名字,点击确定。我们选择基于对话框的MFC应用程序,单击完成,项目创建完成。如下图。5.2程
15、序界面5.3主要的程序代码/ homeworkDlg.cpp : implementation file/#include stdafx.h#include homework.h#include homeworkDlg.h#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE = _FILE_;#endif/#define DELAY 1000#define p(s) :WaitForSingleObject(s,INFINITE)#define v(s) :ReleaseSemaphore(s,1,NUL
16、L)/ CAboutDlg dialog used for App Aboutvoid DrawArrow(CRect rc,CDC *pDC,COLORREF col);void DrawBuffer(int x,int number,CDC * pDC);class CAboutDlg : public CDialogpublic:CAboutDlg();/ Dialog Data/AFX_DATA(CAboutDlg)enum IDD = IDD_ABOUTBOX ;/AFX_DATA/ ClassWizard generated virtual function overrides/A
17、FX_VIRTUAL(CAboutDlg)protected:virtual void DoDataExchange(CDataExchange* pDX); / DDX/DDV support/AFX_VIRTUAL/ Implementationprotected:/AFX_MSG(CAboutDlg)/AFX_MSGDECLARE_MESSAGE_MAP();CAboutDlg:CAboutDlg() : CDialog(CAboutDlg:IDD)/AFX_DATA_INIT(CAboutDlg)/AFX_DATA_INITvoid CAboutDlg:DoDataExchange(C
18、DataExchange* pDX)CDialog:DoDataExchange(pDX);/AFX_DATA_MAP(CAboutDlg)/AFX_DATA_MAPBEGIN_MESSAGE_MAP(CAboutDlg, CDialog)/AFX_MSG_MAP(CAboutDlg)/ No message handlers/AFX_MSG_MAPEND_MESSAGE_MAP()/ CHomeworkDlg dialogCHomeworkDlg:CHomeworkDlg(CWnd* pParent /*=NULL*/): CDialog(CHomeworkDlg:IDD, pParent)
19、/AFX_DATA_INIT(CHomeworkDlg)m_put = 0;DELAY = 0;M = 0;N = 0;m_move1 = 0;m_get2 = 0;m_get3 = 0;m_move2 = 0;P = 0;/AFX_DATA_INIT/ Note that LoadIcon does not require a subsequent DestroyIcon in Win32m_hIcon = AfxGetApp()-LoadIcon(IDR_MAINFRAME);void CHomeworkDlg:DoDataExchange(CDataExchange* pDX)CDial
20、og:DoDataExchange(pDX);/AFX_DATA_MAP(CHomeworkDlg)DDX_Control(pDX, IDC_SPIN9, m_sbbuffer3);DDX_Control(pDX, IDC_SPIN8, m_sbMove2);DDX_Control(pDX, IDC_SPIN7, m_sbGet3);DDX_Control(pDX, IDC_SPIN3, m_sbGet2);DDX_Control(pDX, IDC_SPIN2, m_sbMove1);DDX_Control(pDX, IDC_SPIN6, m_sbbuffer2);DDX_Control(pD
21、X, IDC_SPIN5, m_sbbuffer1);DDX_Control(pDX, IDC_SPIN4, m_delay);DDX_Control(pDX, IDC_SPIN1, m_sbPut);DDX_Text(pDX, IDC_EDIT1, m_put);DDV_MinMaxInt(pDX, m_put, 1, 15);DDX_Text(pDX, IDC_EDIT4, DELAY);DDV_MinMaxInt(pDX, DELAY, 300, 1000);DDX_Text(pDX, IDC_EDIT5, M);DDV_MinMaxInt(pDX, M, 1, 20);DDX_Text
22、(pDX, IDC_EDIT6, N);DDV_MinMaxInt(pDX, N, 1, 20);DDX_Text(pDX, IDC_EDIT2, m_move1);DDV_MinMaxInt(pDX, m_move1, 1, 15);DDX_Text(pDX, IDC_EDIT3, m_get2);DDV_MinMaxInt(pDX, m_get2, 1, 10);DDX_Text(pDX, IDC_EDIT7, m_get3);DDV_MinMaxInt(pDX, m_get3, 1, 10);DDX_Text(pDX, IDC_EDIT8, m_move2);DDV_MinMaxInt(
23、pDX, m_move2, 1, 15);DDX_Text(pDX, IDC_EDIT9, P);DDV_MinMaxInt(pDX, P, 1, 20);/AFX_DATA_MAPBEGIN_MESSAGE_MAP(CHomeworkDlg, CDialog)/AFX_MSG_MAP(CHomeworkDlg)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BUTTON1, OnButton1)ON_BN_CLICKED(IDC_BUTTON2, OnButton2)ON_WM_TIMER()ON_B
24、N_CLICKED(IDC_BUTTON3, OnButton3)/AFX_MSG_MAPEND_MESSAGE_MAP()/ CHomeworkDlg message handlersBOOL CHomeworkDlg:OnInitDialog()CDialog:OnInitDialog();/ Add About. menu item to system menu./ IDM_ABOUTBOX must be in the system command range.ASSERT(IDM_ABOUTBOX & 0xFFF0) = IDM_ABOUTBOX);ASSERT(IDM_ABOUTB
25、OX AppendMenu(MF_SEPARATOR);pSysMenu-AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);/ Set the icon for this dialog. The framework does this automatically/ when the applications main window is not a dialogSetIcon(m_hIcon, TRUE);/ Set big iconSetIcon(m_hIcon, FALSE);/ Set small iconbuffer1number =
26、buffer2number =buffer3number= putid =get2id=get3id =move1id=move2id = 0;alltime=allnumber1=allnumber2=allnumber3 =0;bput =bget2=bget3=bkill=bmove1=bmove2=FALSE;CWnd * pWnd = this-GetDlgItem(IDC_STATIC);pWnd-GetWindowRect(&rcUpdate);this-ScreenToClient(&rcUpdate);m_put = m_get2=m_get3=m_move1=m_move2
27、 =1;M=N=P=5;DELAY =500;this-UpdateData(FALSE);m_sbGet2.SetRange(1,15);m_sbGet3.SetRange(1,15);m_sbMove1.SetRange(1,15);m_sbMove2.SetRange(1,15);m_sbPut.SetRange(1,15);m_delay.SetRange(300,1000);m_sbbuffer1.SetRange(1,20);m_sbbuffer2.SetRange(1,20);m_sbbuffer3.SetRange(1,20);return TRUE; / return TRU
28、E unless you set the focus to a controlvoid CHomeworkDlg:OnSysCommand(UINT nID, LPARAM lParam)if (nID & 0xFFF0) = IDM_ABOUTBOX)CAboutDlg dlgAbout;dlgAbout.DoModal();elseCDialog:OnSysCommand(nID, lParam);/ If you add a minimize button to your dialog, you will need the code below/ to draw the icon. Fo
29、r MFC applications using the document/view model,/ this is automatically done for you by the framework.void CHomeworkDlg:OnPaint() if (IsIconic()CPaintDC dc(this); / device context for paintingSendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);/ Center icon in client rectangleint cxIcon = G
30、etSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;/ Draw the icondc.DrawIcon(x, y, m_hIcon);elseCPaintDC dc(this);COLORREF col;for(int i=1;i=m_put;i+)CRect rc(20,i*10+20
31、,120,(i+1)*10+20);if(putid =i&bput)col=RGB(255,0,255);elsecol=RGB(0,0,205,);DrawArrow(rc,&dc,col);for(int j=1;j=m_get2;j+)CRect rc1(600,j*10+20,700,(j+1)*10+20);if(get2id =j&bget2)col=RGB(255,0,255);elsecol=RGB(0,0,205);DrawArrow(rc1,&dc,col);for(int n=1;n=m_get3;n+)CRect rc2(1100,n*10+20,1200,(n+1)
32、*10+20);if(get3id =n&bget3)col=RGB(255,0,255);elsecol=RGB(0,0,205);DrawArrow(rc2,&dc,col);for(int m=1;m=m_move1;m+)CRect rc3(300,m*10+20,400,(m+1)*10+20);if(move1id=m&bmove1)col=RGB(255,0,255);elsecol=RGB(0,0,205);DrawArrow(rc3,&dc,col);for(int p=1;pSelectObject(&br);pDC-BeginPath();pDC-MoveTo(rc.le
33、ft,rc.top+rc.Height()/4);pDC-LineTo(rc.right-rc.Width()/4,rc.top+rc.Height()/4);pDC-LineTo(rc.right-rc.Width()/4,rc.top);pDC-LineTo(rc.right,rc.top+rc.Height()/2);pDC-LineTo(rc.right-rc.Width()/4,rc.bottom);pDC-LineTo(rc.right-rc.Width()/4,rc.bottom-rc.Height()/4);pDC-LineTo(rc.left,rc.bottom-rc.Hei
34、ght()/4);pDC-LineTo(rc.left,rc.top+rc.Height()/4);pDC-EndPath();pDC-FillPath();pDC-SelectObject(oldBrush);UINT put(LPVOID lparam)CHomeworkDlg * dlg =(CHomeworkDlg *) lparam;int i =dlg-current;while(!dlg-bkill)p(dlg-full1);p(dlg-buffer1);dlg-putid=i;dlg-buffer1number+=1;dlg-bput=TRUE;dlg-allnumber1+;
35、:Sleep(dlg-DELAY);v(dlg-buffer1);v(dlg-empty1);dlg-bput=FALSE;return 0;UINT move1(LPVOID lparam)CHomeworkDlg *dlg = (CHomeworkDlg *)lparam;int i =dlg-current;while(!dlg-bkill)p(dlg-empty1);p(dlg-full2);p(dlg-buffer1);p(dlg-buffer2);dlg-bmove1=TRUE;dlg-buffer1number-;dlg-buffer2number+;dlg-move1id=i;
36、:Sleep(dlg-DELAY);v(dlg-buffer1);v(dlg-buffer2);v(dlg-empty2);v(dlg-full1);dlg-bmove1=FALSE;return 0;UINT move2(LPVOID lparam)CHomeworkDlg *dlg = (CHomeworkDlg *)lparam;int i =dlg-current;while(!dlg-bkill)p(dlg-empty1);p(dlg-full3);p(dlg-buffer1);p(dlg-buffer3);dlg-bmove2=TRUE;dlg-buffer1number-;dlg
37、-buffer3number+;dlg-move2id=i;:Sleep(dlg-DELAY);v(dlg-buffer1);v(dlg-buffer3);v(dlg-empty3);v(dlg-full1);dlg-bmove2=FALSE;return 0;UINT get2(LPVOID lparam)CHomeworkDlg * dlg = (CHomeworkDlg *)lparam;int i =dlg-current;while (!dlg-bkill)p(dlg-empty2);p(dlg-buffer2);dlg-get2id=i;dlg-buffer2number-;dlg
38、-bget2=TRUE;dlg-allnumber2+;:Sleep(dlg-DELAY);v(dlg-buffer2);v(dlg-full2);dlg-bget2=FALSE;return 0;UINT get3(LPVOID lparam)CHomeworkDlg * dlg = (CHomeworkDlg *)lparam;int i =dlg-current;while (!dlg-bkill)p(dlg-empty3);p(dlg-buffer3);dlg-get3id=i;dlg-buffer3number-;dlg-bget3=TRUE;dlg-allnumber3+;:Sleep(dlg-DEL