第十七课:多线程
一、概念:
进程:一个进程就是一个运行的程序,它有独立的内存、文件句柄和其他系统资源。当启动一个进程时,操作系统会为此进程建立一个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;
}
评论