第十七课:多线程 一、概念: 进程:一个进程就是一个运行的程序,它有独立的内存、文件句柄和其他系统资源。当启动一个进程时,操作系统会为此进程建立一个4GB的地址空间,进程是操作系统分配内存地址空间的单位。 线程:是操作系统分配处理器时间的最基本单元。所以一个进程必须包含一个线程,我们称之为主线程。如果需要,进程可以产生更多的线程,让CPU在同一时间执行 不同段落的代码。 二、举例: 1. 在单线程中执行多个死循环: 建立一个控制台工程,加入一个cpp文件,编写如下代码: #include "stdio.h" void fun() { while(1) { printf("thread 2 is rurnning!\n"); } } void main() { fun(); while(1) { printf("thread 1 is running!\n"); } } 此时只能打出thread 2 is rurnning!,因为CPU总是在执行fun函数的代码 。没有机会向下执行其余代码。 2. 在单线程中执行多个死循环: #include "stdio.h" #include "windows.h" DWORD WINAPI fun(LPVOID lpParameter) { while(1) { printf("thread 2 is rurnning!\n"); } } void main() { CreateThread(NULL,0,fun,0,0,0); while(1) { printf("thread 1 is running!\n"); } } 此时“thread 1 is rurnning!”,“thread 2 is rurnning!”都能打出来,两个死循环在同时运行。 三、编写一个聊天程序: 1. 建立一个基于对话框的MFC工程,删除提示用的标签控件。加入一个用于显示聊天对方发来的信息的列表框,一个用于输入输入发送给别人的信息的文本框,一个用于输入IP地址的IP Address控件。 2. 定义一个成员变量 SOCKET m_sockChat; 3. 在对话框中加入一个成员函数InitSocket(),对m_sockChat进行初始化: m_sockChat=socket(AF_INET,SOCK_DGRAM,0); SOCKADDR_IN addrChat; addrChat.sin_family=AF_INET; addrChat.sin_port=htons(3000); addrChat.sin_addr.S_un.S_addr=htonl(INADDR_ANY); bind(m_sockChat,(SOCKADDR*)&addrChat,sizeof(SOCKADDR)); 4. 加入OnOK函数,注释其中的CDialog::OnOK();语句,然后加入代码: CString str; GetDlgItemText(IDC_EDIT1,str); //得到用户在文本框中输入的字符串 SOCKADDR_IN addrDest; addrDest.sin_family=AF_INET; addrDest.sin_port=htons(3000); DWORD dwIP; ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP); //得到用户在IP地址控件中输入的地址 addrDest.sin_addr.S_un.S_addr=htonl(dwIP); sendto(m_sockChat,str,str.GetLength(), 0,(SOCKADDR*)&addrDest,sizeof(SOCKADDR)); //发送数据到达指定的计算机 SetDlgItemText(IDC_EDIT1,""); //将文本框清空 5. 定义一个结构体,用于向线程函数传递参数: struct RECVPARAM { SOCKET sock; HWND hwnd; }; 6. 在对话框的OnInitDialog()函数中加入: InitSocket(); //初始化socket ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->SetAddress(192,168,8,255); //设置IP地址控件的初始地址 RECVPARAM *precvparam=new RECVPARAM(); precvparam->sock=m_sockChat; precvparam->hwnd =this->m_hWnd; ::CreateThread(NULL,0,recvproc,(LPVOID)precvparam,0,0); //启动一个线程,在线程函数中接收数据 7. 定义线程函数: DWORD WINAPI recvproc(LPVOID lpParameter) { SOCKET sockChat=((RECVPARAM*)lpParameter)->sock; HWND hwnd=((RECVPARAM*)lpParameter)->hwnd; //取出传递进来的参数 detele (RECVPARAM*)lpParameter; char buf[1024]; //定义用于接收数据的缓冲区 SOCKADDR_IN addrSrc; int len,x; char *str; while (1) { memset(buf,0,1024); //清空缓冲区 len=sizeof(SOCKADDR); recvfrom(sockChat,buf,1024,0,(SOCKADDR*)&addrSrc,&len); //接收数据 sprintf(buf,"%s:%s:%d",buf, inet_ntoa(addrSrc.sin_addr),ntohs(addrSrc.sin_port)); //得到对方的IP地址和端口号 x=strlen(buf); str=new char[x+1]; strcpy(str,buf); //将得到的数据转存 ::PostMessage(hwnd,WM_USER+1,0,(LPARAM)str); //向指定的窗口发送消息 } } 8. 在对话框的消息映射表中添加: ON_MESSAGE(WM_USER+1, OnRecvData) 9. 在对话框中添加成员函数OnRecvData: LRESULT CChatDlg::OnRecvData(WPARAM wParam, LPARAM lParam) { ((CListBox*)GetDlgItem(IDC_LIST1))->InsertString(0,(char*)lParam); //将接收到的信息加入列表框中 delete (char*)lParam; //释放在线程中用new分配的空间 return 1; }

评论