在window环境下,Win32 提供了一系列的API函数来完成线程的创建、挂起、恢复、终结以及通信等工作:
序号 | 函数名 | 功能 |
1 | CreateThread() | 创建一个新线程 |
2 | ExitThread() | 正常结束一个线程的执行 |
3 | TerminateThead() | 强制终止一个线程的执行 |
4 | ResumeThread() | 重启一个线程 |
5 | SuspendThread() | 挂起一个线程 |
6 | GetExiCodeThread() | 得到一个线程的退出码 |
7 | GetThreadPriority() | 得到一个线程的优先级 |
8 | SetThreadPriority() | 设置一个线程的优先级 |
9 | CloseHandle() | 关闭一个线程的句柄 |
10 | CreateRemoteThread() | 再另一个进程中创建一个新线程 |
11 | PostThreadMessage() | 发送一条消息给指定的线程 |
12 | GetCurrentThread() | 得到当前的线程句柄 |
13 | GetCurrentThreadId() | 得到当前线程的ID |
14 | GetThreadId() | 得到指定线程的ID |
15 | WaitForSingleObject() | 等待单个对象 |
16 | WaitForMultipleObjects() | 等待多个对象 |
关于多线程的API函数还有很多,以上只是列出了一些比较常用的函数,欲知更多函数和函数的使用方法,请参考MSDN或网络资源,在此就不再介绍了。
线程函数的规范格式定义为
DWORD WINAPI ThreadProc (LPVOID lpParam);//格式不正确将无法调用成功。函数名称没有限制,只要符合命名规则就可以。
但我常常看到有下列的线程函数定义:
void ThreadProc ();//该格式也是可以的,但使用的时候要这样通过
LPTHREAD_START_ROUTINE转换,如:
(LPTHREAD_START_ROUTINE)ThreadProc
我建议还是使用规范的格式比较好,不推荐使用void ThreadProc ()格式。不信就请看看MSDN的说明吧:
Do not declare this callback function with a void return typeand cast the function pointer toLPTHREAD_START_ROUTINE when creatingthe thread. Code that does this is common, but it can crash on 64-bit Windows.
而且线程函数必须是全局函数,不能在类中声明和定义。
我在此将写一个简单的多线程程序,用以展示多线程的功能和使用方法。该程序的主要的思想是画3个进度条,分别以多线程和单线程方式完成,大家可以比较一下。
说明:
(1)该程序还将和单线程做对比。
(2)由于给线程的函数传递了多个参数,所以采用结构体的方式传递参数。
(3)为了演示效果,采用了比较耗时的打点处理。
主要的函数如下:
在头文件的定义
//实现文件中
运行效果:
单线程测试
多线程测试
工程源码下载地址:
http://download.csdn.net/detail/cbnotes/4857152
欢迎大家修改和指正。
注意事项:
(1)传递给线程执行函数的参数不能是局部变量,而且必须是参数的地址。如:
Int nOffset = 10;
CreateThread(NULL,0,ThreadProc,nOffset,0,&dwThreadID[i]);//错误
CreateThread(NULL,0,ThreadProc,&nOffset,0,&dwThreadID[i]);//错误
Int *pOffset = newint(10);
CreateThread(NULL,0,ThreadProc,pOffset,0,&dwThreadID[i]);//正确
(2)线程执行函数必须是全局函数。
(3)请大家改改下面的程序,且解释下为什么?
这是我开始写程序遇到的一个问题,
改写上面的函数:只是将结构体中一个参数改为CDC指针,以便直接调用。
struct threadInfo
{
CDC * dc; //画布
int nOffset; //偏移量
COLORREF clrRGB; //颜色
};
//多线程测试
void CMultiThread_1Dlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
CDC *dc = GetDC();
CRect rt;
GetClientRect(rt);
dc->FillSolidRect(0,0,rt.Width(),rt.Height()-70,RGB(240,240,240));//刷新背景
dc->TextOut(97,470,"#1");
dc->TextOut(297,470,"#2");
dc->TextOut(497,470,"#3");
//初始化线程的参数
Info[0].dc = Info[1]dc = Info[2].dc = dc;
Info[0].nOffset = 10;Info[1].nOffset = 210;Info[2].nOffset = 410;
Info[0].clrRGB = RGB(255,0,0);Info[1].clrRGB= RGB(0,255,0);Info[2].clrRGB = RGB(0,0,255);
//创建线程
for (int i &#61; 0;i<3;i&#43;&#43;)
{
hThead[i] &#61; CreateThread(NULL,0,ThreadProc,&Info[i],0,&dwThreadID[i]);
}
//ReleaseDC(dc);
}
//线程执行函数
DWORD WINAPI ThreadProc(LPVOIDlpParam)
{
threadInfo*Info &#61; (threadInfo*)lpParam;
for (int i&#61;0;i<460;i&#43;&#43;)
{
for (int j&#61;Info->nOffset;j<Info->nOffset&#43;190;j&#43;&#43;)
{
Info->dc->SetPixel(j,460-i,Info->clrRGB);
}
}
return 0;
}
运行结果&#xff1a;
为什么会这样呢&#xff1f;我还没有找到答案&#xff0c;望大家能给我个解释&#xff0c;谢谢。
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
该实例将演示一个简单的多线程协同工作的例子&#xff0c;以供大家学习和参考。大致原理是&#xff1a;5个人开始比赛&#xff08;比如赛跑&#xff09;&#xff0c;谁先完成比赛就结束&#xff0c;并统比赛时间和赢者。主线程用于界面的相关显示&#xff0c;5个线程模拟5个人的行为&#xff08;赛跑&#xff09;&#xff0c;另外一个线程用于检测5个线程的运行情况&#xff0c;只要有人到达终点&#xff0c;比赛就结束并做相关的技术统计。
主要函数为&#xff1a;
MulitThread_2Dlg.h : 头文件
//声明线程处理函数
DWORD WINAPI ThreadProc1(LPVOIDlpParam);
DWORD WINAPI ThreadProc2(LPVOIDlpParam);
//为了传递多个参数&#xff0c;我采用结构体
struct threadInfo1
{
HWND hWnd; //窗口句柄
int nOffset; //偏移量
};
struct threadInfo2
{
HWND hWnd; //窗口句柄
HANDLE *phHandle; //偏移量
};
protected:
long m_nTime;//时间
HANDLE m_hThead[5]; //用于存储线程句柄
HANDLE hThead; //用于存储线程句柄
DWORD m_dwThreadID[5];//用于存储线程的ID
threadInfo1Info1[5]; //传递给线程处理函数的参数
threadInfo2Info2;
// MulitThread_2Dlg.cpp : 实现文件
//更新时间&#xff1a;毫秒
void CMulitThread_2Dlg::OnTimer(UINT_PTRnIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_nTime&#43;&#61;100;//毫秒为单位
CString str;
str.Format("时间&#xff1a;%.1f秒",m_nTime/1000.0);
GetDlgItem(IDC_STATIC2)->SetWindowText(str);
CDialog::OnTimer(nIDEvent);
}
//消息处理函数
LRESULT CMulitThread_2Dlg::OnGameOver(WPARAMwParam, LPARAMlParam)
{
KillTimer(1);//关闭计时器
if (wParam &#61;&#61;0)
{//出错
GetDlgItem(IDC_STATIC1)->SetWindowText("出错啦!");
GetDlgItem(IDC_STATIC2)->SetWindowText("---");
AfxMessageBox("出错啦!",MB_OK|MB_ICONERROR);
}
else
{//成功
//显示结果
char *pName[] &#61; {"张三","李四","王二","小蔡","赵干"};
CStringstr;
str.Format("赢者&#xff1a;%s",pName[lParam]);
GetDlgItem(IDC_STATIC1)->SetWindowText(str);
}
//使能开始按钮&#xff0c;以便可以开始下一次比赛
GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE);
return 0;
}
//开始比赛
void CMulitThread_2Dlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
//使能开始按钮&#xff1a;无效
GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);
m_nTime &#61;0;//初始化时间为
CDC *dc &#61; GetDC();
CRect rt;
GetClientRect(rt);
dc->FillSolidRect(40,0,rt.Width()-49,rt.Height()-50,RGB(240,240,240));//刷新背景
ReleaseDC(dc);
//初始化线程的参数
Info1[0].hWnd &#61; Info1[1].hWnd &#61; Info1[2].hWnd &#61; Info1[3].hWnd &#61; Info1[4].hWnd &#61; GetSafeHwnd();
Info1[0].nOffset &#61; 0;Info1[1].nOffset &#61; 90;Info1[2].nOffset &#61; 180;Info1[3].nOffset &#61; 270;Info1[4].nOffset &#61; 360;
//创建线程
for (int i &#61; 0;i<5;i&#43;&#43;)
{
m_hThead[i] &#61; CreateThread(NULL,0,ThreadProc1,&Info1[i],CREATE_SUSPENDED,&m_dwThreadID[i]);
}
SetTimer(1,100,NULL);//开始计时
GetDlgItem(IDC_STATIC1)->SetWindowText("进行中...");
//开始运行
for (int i &#61; 0;i<5;i&#43;&#43;)
{
ResumeThread(m_hThead[i]);
}
//开始运行监测结果线程
Info2.hWnd &#61; m_hWnd;
Info2.phHandle &#61; m_hThead;
hThead &#61; CreateThread(NULL,0,ThreadProc2,&Info2,0,NULL);
}
//比赛线程
DWORD WINAPI ThreadProc1(LPVOIDlpParam)
{
threadInfo1*info &#61; (threadInfo1*)lpParam;
CDC *dc &#61; CWnd::FromHandle(info->hWnd)->GetDC();
for (int i&#61;40;i<570;i&#43;&#61;2)
{
for (int j&#61;0;j<1000;j&#43;&#43;)
{//重复操作&#xff0c;以便人眼观察
dc->Rectangle(CRect(i,info->nOffset,i&#43;1,info->nOffset&#43;80));
}
}
DeleteObject(dc);
return 0;
}
//监视线程&#xff1a;谁先完成比赛就结束
DWORD WINAPI ThreadProc2(LPVOIDlpParam)
{
threadInfo2*info &#61; (threadInfo2*)lpParam;
DWORD dwRet &#61; 0;
//等待个线程中的一个完成
dwRet &#61; WaitForMultipleObjects(5,info->phHandle,FALSE,INFINITE);
if (dwRet &#61;&#61; WAIT_FAILED)
{//出错啦
::SendMessage(info->hWnd,WM_GAMEOVER,0,0);
return 0;
}
//终止各个线程
for (int i&#61;0;i<5;i&#43;&#43;)
{
TerminateThread(info->phHandle[i],0);
}
//发送比赛结果消息
::SendMessage(info->hWnd,WM_GAMEOVER,1,dwRet- WAIT_OBJECT_0);
return 0;
}
运行结果&#xff1a;
工程源码下载地址&#xff1a;
http://download.csdn.net/detail/cbnotes/4867333
欢迎大家修改和指正。
注意事项&#xff1a;
1. 该程序连主线程一共7个线程。其中一个线程专门用于检测5个比赛线程的运行结果检测&#xff0c;为什么要专门开这个线程而不在主线程中进行呢&#xff1f;主要是WaitForMultipleObjects()函数是一个阻塞函数&#xff0c;如果在主线程中运行该函数&#xff0c;将使整个程序的界面不能操作&#xff08;如&#xff1a;不能移动窗口等&#xff09;&#xff0c;因为一直阻塞在WaitForMultipleObjects函数处&#xff0c;而不能处理其它消息&#xff0c;不信大家可以试试。
2. 线程同步的两个比较重要的函数为WaitForSingleObject()和WaitForMultipleObjects()&#xff0c;具体使用请参考MSDN。这两个函数都是阻塞函数&#xff0c;一直等待授信的对象发生才返回。
3. 采用消息的方式通知主线程的运行结果&#xff0c;该方法比较简单有效。一般的多线程程序都是采用主线程负责显示&#xff0c;辅助线程来完成比较耗时的任务&#xff0c;等任务完成后再通知主线程运行结果。
转载请说明出处&#xff0c;谢谢。