




已阅读5页,还剩49页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
不同I/O模式下的处理方法,阻塞模式与非阻塞模式 多进程与多线程 I/O阻塞模式的多线程网络编程方法 I/O非阻塞模式的异步处理模型,第5章,5.1 阻塞模式与非阻塞模式,Winsock在进行I/O操作的时候,可以使用两种工作模式: 一种是阻塞模式,也称同步模式; 另一种是非阻塞模式,也称异步模式。,5.1.1 阻塞模式及其优缺点,阻塞模式下,在I/O操作完成之前,不会立即交出CPU的控制权。 阻塞套接字的I/O操作确定,容易编程; 以下情况性能低下: 需要建立多个套接字连接来为多个客户机服务的时候,或在数据的收发量不均匀的时候,或在I/O的时间不确定的时候。,5.1.2 非阻塞模式及其优缺点,非阻塞模式下,当进程调用了一个Winsock的I/O函数时,无论I/O操作是否能够完成,执行操作的Winsock函数都会立即返回调用它的程序。 非阻塞模式下的函数调用会频繁地返回错误,所以在任何时候,都应做好“失败”的准备,并仔细检查返回代码。,5.2 多进程与多线程,从操作系统基本运行单元角度来看,并发的实现方式主要有两种: 一种是多进程并发 一种是多线程并发,原则上一个CPU只能分配给一个进程,同时运行多个进程,就必须使用并发技术。 操作系统一般采用“时间片轮转进程调度算法”实现并发。,5.2.1 什么是多进程,5.2.2 什么是多线程,多线程可以实现并行处理,避免了某项任务长时间占用CPU时间。 并行运行的效率显然高于并发运行,所以在多CPU的计算机中,多任务的效率比较高。,5.2.3 多进程和多线程的关系(1),线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间。 线程是进程内部的一个执行单元,是CPU调度和分派的基本单位。 每一个进程至少有一个主线程,它无需由用户去主动创建,是由系统自动创建的。,5.2.3 多进程和多线程的关系(2),进程和线程二者差异,如下几个方面: 首先,进程在执行过程中拥有独立的内存单元,而多个线程共享内存。 其次,线程的划分尺度小于进程。 第三,线程的执行效率比进程要高。 最后,操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。,问题的提出,新建一个基于对话框的应用程序SingleThread,在主对话框 添加一个按钮, 标题为“延时6秒”,添加按钮的响应函数,代码如下: void CSingleThreadDlg:OnSleepSixSecond() Sleep(6000); /延时 6秒 编译并运行应用程序,单击“延时6秒”按钮,这6秒期间程序就象“死机”一样,不在响应其它消息。,5.2.4 网络编程采用多线程机制的重要性,服务器端 客户端,图5-1 多线程服务器与多个客户机通信,Win32 API对多线程编程的支持,Win32 提供了一系列的API函数来完成线程的创建、挂起、恢复、终结以及通信等工作。 下面将选取其中的一些重要函数进行说明。,1、HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId); 如果创建成功则返回线程的句柄,否则返回 NULL。,该结构决定了线程的安全属性,一般置为 NULL,该参数返回所创建线程的ID,指定了线程的堆栈深度,一般都设置为0,表示新线程开始执行时代码所在函数的地址,线程函数的参数,如果该参数为0,线程在被创建后就会立即开始执行;如果该参数为CREATE_SUSPENDED,则系统产生线程后,该线程处于挂起状态,并不马上执行,直至函数 ResumeThread被调用,2、DWORD SuspendThread(HANDLE hThread); 该函数用于挂起指定的线程,如果函数执行成功,则线程的执行被终止。 3、 DWORD ResumeThread(HANDLE hThread); 该函数用于结束线程的挂起状态,执行线程。 4、VOID ExitThread(DWORD dwExitCode); 该函数用于线程终结自身的执行,主要在线程的执行函数中被调用。其中参数dwExitCode用来设置线程的退出码。,5、 BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode); 一般情况下,线程运行结束之后,线程函数正常返回,但是应用程序可以调用TerminateThread强行终止某一线程的执行。各参数含义如下: hThread:将被终结的线程的句柄; dwExitCode:用于指定线程的退出码。 使用 TerminateThread()终止某个线程的执行是不安全的,可能会引起系统不稳定;虽然该函数立即终止线程的执行,但并不释放线程所占用的资源。因此,一般不建议使用该函数。,6、BOOL PostThreadMessage(DWORD idThread, UINT Msg, WPARAM wParam, LPARAM lParam); 该函数将一条消息放入到指定线程的消息队列中,并且不等到消息被该线程处理时便返回。 idThread:将接收消息的线程的ID; Msg:指定用来发送的消息; wParam:同消息有关的字参数; lParam:同消息有关的长参数; 调用该函数时,如果即将接收消息的线程没有创建消息循环,则该函数执行失败。,例程1 MultiThread1,建立一个基于对话框的工程MultiThread1,在对话框IDD_MULTITHREAD1_DIALOG中加入两个按钮和一个编辑框,两个按钮的ID分别是IDC_START,IDC_STOP ,标题分别为“启动”,“停止”,IDC_STOP的属性选中Disabled;编辑框的ID为 IDC_TIME ,属性选中Read-only; 在MultiThread1Dlg.h文件中添加线程函数声明: void ThreadFunc(); 线程函数的声明应在类CMultiThread1Dlg的外部。 在类CMultiThread1Dlg内部添加protected型变量: HANDLE hThread; DWORD ThreadID; 分别代表线程的句柄和ID。 在MultiThread1Dlg.cpp文件中添加全局变量 m_bRun : volatile BOOL m_bRun; m_bRun 代表线程是否正在运行。,编写线程函数: void ThreadFunc() CTime time; CString strTime; m_bRun=TRUE; while(m_bRun) time=CTime:GetCurrentTime(); strTime=time.Format(“%H:%M:%S“); :SetDlgItemText(AfxGetMainWnd()-m_hWnd,IDC_TIME,strTime); Sleep(1000); 该线程函数没有参数,也不返回函数值。只要m_bRun为TRUE,线程一直运行。,双击IDC_START按钮,完成该按钮的消息函数: void CMultiThread1Dlg:OnStart() hThread=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFunc, NULL, 0, ,双击IDC_STOP按钮,完成该按钮的消息函数: void CMultiThread1Dlg:OnStop() m_bRun=FALSE; GetDlgItem(IDC_START)-EnableWindow(TRUE); GetDlgItem(IDC_STOP)-EnableWindow(FALSE); ,5.3 I/O阻塞模式的多线程网络编程方法,MFC支持的两种线程 创建MFC的工作线程 创建并启动用户界面线程 终止线程 FTP客户机多线程编程实例,5.3.1 MFC支持的两种线程,MFC对多线程进行了一层简单的封装,在Visual C+中每个线程都是从CWinThread类继承而来的。每一个应用程序的执行都有一个主线程,这个主线程也是从CWinThread类继承而来的。可以利用CWinThread对象创建应用程序执行的其它线程。,利用MFC可以创建两种线程,用户接口线程 用户界面线程一般用于处理独立于其他线程执行之外的用户输入,响应用户及系统所产生的事件和消息等。 工作线程 工作线程没有消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等。 但对于Win32的API编程而言,这两种线程是没有区别的,它们都只需线程的启动地址即可启动线程来执行任务。,5.3.2 创建MFC的工作线程,编程实现控制函数 一个工作线程对应一个控制函数,线程执行的任务都应编写在控制函数之中。 控制函数的原型是: UINT ControlFunctionName( LPVOID pParam );,5.3.2 创建MFC的工作线程,创建并启动工作线程 AfxBeginThread( )函数就可以创建新的线程,并使新线程开始运行。,CWinThread * AfxBeginThread ( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int pPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTTRIBUTES lpSecurityAttrs = NULL );,要创建并启动工作线程, AfxBeginThread( )函数必须采用如下的调用格式:,创建工作线程的例子,(1) 编程实现线程控制函数。 首先定义一个结构: struct int nN; / 数组元素的个数 double * pD; / 指向一个双精度 实数的数组 myData; 然后定义此结构类型的变量, 并省略了对该变量的初始化代码: myData ss; 接着定义线程的控制函数:,UINT MyCalcFunc(LPVOID pParam) / 如果入口参数为空指针,终止线程 if (pParam = NULL) AfxEndThread(MY_NULL_POINTER_ERROR); int nN = pParam-nN; / 数组的元素个数 double * pD = pParam-pD;/ 指向数组的第一个元素 double sum=0; / 数组元素之和 for (int i =0; i nN; i+) / 求和 sum += pDi; CString bb; / 格式化显示字符串 bb.Format(“数组的和是:%d“, sum); / 显示结果 AfxMessageBox(bb); return 0; ,(2) 在程序进程的主线程中调用 AfxBeginThread(MyCalcFunc, 一旦调用了此函数,线程就被创建,并开始执行线程函数。当数据的计算完成时,函数将停止运行,相应的线程也随即终止。,4创建工作线程的一般模式 : (1) 构建工作线程控制函数的框架 UINT MyThreadProc(LPVOID PPARAM) CMyObject * pObject = (CMyObject *) pParam; / 传递参数 if(pObject = NULL |!pObject-IsKindOf(RUNTIME_CLASS(CMyObject) return 1; return 0; (2) 在程序的另一个函数中插入相关代码 pNewObject = new CMyObject; AfxBeginThread(MyThreadProc, pNewObject);,练习,查找N(很大)以内的素数,N的取值很大(比如:10000等),并在主界面实时动态显示处理结果,并显示处理进度等。,5.3.3 创建并启动用户界面线程,从CWinThread类派生出自己的线程类 改造自己的线程类 改造.h头文件(用宏DECLARE_DYNCREATE对该类进行声明) 改造.CPP实现文件(用宏IMPLEMENT_DYNCREATE对该类进行实现) 重载基类(CWinThread类)的某些成员函数,5.3.3 创建并启动用户界面线程,改造自己的线程类 创建新的用户界面窗口类,如窗口、对话框,并添加所需要的用户界面控件,然后建立新建的线程类与这些用户界面窗口类之间的联系。 利用类向导,为新建的线程类添加控件成员变量,添加响应消息的成员函数,为它们编写实现的代码。 经过以上步骤的改造,用户的线程类已经具备了完成用户任务的能力.,5.3.3 创建并启动用户界面线程,创建并启动用户界面线程 要创建并启动用户界面线程,可以使用MFC提供的AfxBeginThread( )函数的另一个版本,其调用格式是:,CWinThread * AfxBeginThread( CRuntimeClass * pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTTRIBUTES lpSecurityAttrs = NULL );,5.3.4 终止线程,正常终止线程 提前终止线程,实例,先从CWinThread类派生一个类,然后调用AfxBeginThead()创建CWinThread派生类的对象进行初始化,启动线程运行。该实例主要演示文件的移动,并显示操作的进度。 设计到两个线程:文件的读写采用工作线程,进度显示采用用户界面线程。,5.3.4 FTP客户机多线程编程实例,在程序中,应重点注意,如何编写线程的控制函数,如何创建一个新的线程,线程如何传递参数和取回结果。,5.4 I/O非阻塞模式的异步处理模型,select模型 WSAAsyncSelect异步I/O模型 WSAEventSelect事件选择模型,5.4.1 Select模型,select( )函数 select( )函数原型如下,其中,fd_set数据类型代表着一系列特定套接字的集合。,int select( int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, const struct timeval * timeout );,timeval结构的定义是: struct timeval long tv_sec; / 以秒为单位指定等待时间 long tv_usec; / 以毫秒为单位指定等待时间 ;,5.4.1 Select模型,操作套接字集合的宏 Winsock提供了下列宏操作,专门对fd_set数据类型进行操作 FD_CLR(s, *set) FD_ISSET(s, *set) FD_SET(s, *set) FD_ZERO(*set),5.4.1 Select模型,select模型的操作步骤 使用FD_ZERO宏,初始化自己感兴趣的每一个fd_set集合。 使用FD_SET宏,将要检查的套接字句柄添加到自己感兴趣的每个fd_set集合中。 调用select( )函数,然后等待。 根据select( )的返回值,便可判断出哪些套接字存在着尚未完成(待决)的I/O操作。 对相应的套接字的I/O进行处理,然后返回步骤(1),继续进行select处理。,从网络上接受数据写入一个文件中。,练习,从网络上接受数据写入一个文件中。,5.4.2 WSAAsyncSelect异步I/O模型,异步I/O模型通过调用WSAAsyncSelect( )函数实现。利用这个模型,应用程序可在一个套接字上,接收以Windows消息为基础的网络事件通知。 WSAAsyncSelect( )函数,int WSAAsyncSelect( SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent );,例如: WSAAsyncSelect(s, hwnd, WM_SOCKET, FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE);,5.4.2 WSAAsyncSelect异步I/O模型,窗口回调例程,LRESULT CALLBACK WindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );,举例,#define WM_SOCKET WM_USER + 1 / 自定义一个消息 #include / 程序的主函数 int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) SOCKET Listen; /定义监听套接字 HWND Window; /定义窗口句柄 struct sockaddr_in InternetAddr /定义地址结构变量 / 创建一个窗口,并将ServerWinProc回调例程分配到该窗口名下 Window = CreateWindow(.); / 初始化Winsock,并创建套接字 WSAStartup(.); Listen = socket( ); / 将套接字绑定到5150端口 InternetAddr.sin_family = AF_INET; InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY); InternetAddr.sin_port = htons(5150); bind(Listen, (struct sockaddr*),/ 对监听套接字,使用上面定义的WM_SOCKET,调用WSAAsyncSelect( ) / 函数打开窗口消息通知,注册的事件是FD_ACCEPT和FD_CLOSE WSAAsyncSelect(Listen, Window, WM_SOCKET, FD_ACCEPT | FD_CLOSE); / 启动套接字的监听 listen(Listen, 5); / 翻译并发送Window消息,直到应用程序终止 ,/ 所创建窗口的回调例程 BOOL CALLBACK ServerWinProc(HWND hDlg, WORD wMsg,WORD waram, DWORD lParam) SOCKET Accept; / 定义服务器端的连接套接字 switch(wMsg) case WM_PAINT: / 处理window paint 消息 break; case WM_SOCKET: / 使用WSAGETSELECTERROR宏来决定在套接字上是否发生 / 错误 if (WSAGETSELECTERROR(lParam) / 显示错误信息并且关闭套接字 closesocket(wParam); break; / 决定在该套接字上出现了什么事件,switch(WSAGETSELECTEVENT(lParam) case FD_ACCEPT: / 表示监听套接字收到了一个连接请求,接收它,产生连接 / 套接字 Accept = accept(wParam, NULL, NULL); / 为连接套接字注册读、写和关闭事件,启动消息通知 WSAAsyncSelect(Accept, hwnd, WM_SOCKET, FD_READ | FDWRITE |FD_CLOSE); break; case FD_READ: / 表示数据已到,可从wParam中的套接字接收数据 break;,case FD_WRITE: / 表示在wParam中的套接字已经准备好发送数据 break; case FD_CLOSE: / 表示连接已经关闭,可关闭套接字 closesocket(wParam); break; break; return TRUE; ,5.4.3 WSAEventSelect事件选择模型,WSAEventSelect事件选择模型和WSAAsyncSelect模型类似,最主要的差别在于,网络事件会投递至一个事件对象句柄,而非投递至一个窗口例程。 编程步骤 创建事件对象句柄 WSAEVENT WSACreateEvent(void);,5.4.
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 《艺术家心中的自画像》课件
- 双十二银行优惠大盘点
- 双十二房产投资解析
- 防艾滋病教育班会
- 幼儿园小班科学《认识萝卜》课件
- 《继电器原理及其应用》课件
- 《杰出领袖的商业智慧》课件
- 2025年聘请咨询服务合同的范本
- 《广告牌制作教程》课件
- 2025技术许可合同模板
- 主动脉夹层病人的健康宣教
- 《危险化学品企业安全生产标准化规范》专业深度解读与应用培训指导材料之4:5管理要求-5.3 安全生产信息与合规审核(雷泽佳编制-2025A0)
- 《危险化学品企业安全生产标准化规范》专业深度解读与应用培训指导材料之3:5管理要求-5.2 安全生产责任制(雷泽佳编制-2025A0)
- 2025年体育产业信息化管理计划
- 2024年内蒙古建投国电准格尔旗能源有限公司招聘考试真题
- 云南省烟草专卖局(公司)2025年上半年高校毕业生招聘(第二批)易考易错模拟试题(共500题)试卷后附参考答案
- 陕西、山西省天一大联考2024-2025学年高中毕业班阶段性测试(七)英语试题及答案
- 2025年企业安全生产知识竞赛全套复习题库及答案(完整版)
- 酒店培训技巧
- 车内日常卫生管理制度
- 客运资格考试题及答案
评论
0/150
提交评论