采用CreateThread()创建多线程程序[通俗易懂]

采用CreateThread()创建多线程程序[通俗易懂]采用CreateThread()创建多线程程序在window环境下,Win32提供了一系列的API函数来完成线程的创建、挂起、恢复、终结以及通信等工作:1、主要的函数列表:序号函数名功能1CreateThread()创建一个新线程2ExitThread()正

大家好,又见面了,我是你们的朋友全栈君。

采用CreateThread()创建多线程程序

在window环境下,Win32 提供了一系列的API函数来完成线程的创建、挂起、恢复、终结以及通信等工作:

1、主要的函数列表:

序号

函数名

功能

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或网络资源,在此就不再介绍了。

2、线程函数的定义:

线程函数的规范格式定义为

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 to LPTHREAD_START_ROUTINE when creatingthe thread. Code that does this is common, but it can crash on 64-bit Windows.

而且线程函数必须是全局函数,不能在类中声明和定义。

3、多线程实例1:

我在此将写一个简单的多线程程序,用以展示多线程的功能和使用方法。该程序的主要的思想是画3个进度条,分别以多线程和单线程方式完成,大家可以比较一下。

说明:

(1)该程序还将和单线程做对比。

(2)由于给线程的函数传递了多个参数,所以采用结构体的方式传递参数。

(3)为了演示效果,采用了比较耗时的打点处理。

主要的函数如下:

在头文件的定义

//线程函数声明DWORD WINAPI ThreadProc(LPVOIDlpParam);//为了传递多个参数,我采用结构体struct threadInfo{    HWND hWnd;       //窗口句柄    int  nOffset;    //偏移量    COLORREF clrRGB; //颜色}; protected:HANDLE hThead[3];    //用于存储线程句柄    DWORD  dwThreadID[3];//用于存储线程的ID  threadInfo Info[3];   //传递给线程处理函数的参数

//实现文件中

//单线程测试void CMultiThread_1Dlg::OnBnClickedButton1(){    // TODO: 在此添加控件通知处理程序代码    //使能按钮    GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);    GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE);    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");    //#1    for (int i=0;i<460;i++)    {       for (int j =10 ;j<200;j++)       {           dc->SetPixel(j,460-i,RGB(255,0,0));       }    }    //#2    for (int i=0;i<460;i++)    {       for (int j =210 ;j<400;j++)        {           dc->SetPixel(j,460-i,RGB(0,255,0));       }    }    //#3    for (int i=0;i<460;i++)    {       for (int j =410 ;j<600;j++)       {           dc->SetPixel(j,460-i,RGB(0,0,255));       }    }    ReleaseDC(dc);    //使能按钮    GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE);    GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE);} //多线程测试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].hWnd = Info[1].hWnd = Info[2].hWnd = GetSafeHwnd();    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 = 0;i<3;i++)    {       hThead[i] = CreateThread(NULL,0,ThreadProc,&Info[i],0,&dwThreadID[i]);    }    ReleaseDC(dc);} DWORD WINAPI ThreadProc(LPVOIDlpParam){    threadInfo*Info = (threadInfo*)lpParam;    CDC *dc = CWnd::FromHandle(Info->hWnd)->GetDC();    for (int i=0;i<460;i++)    {       for (int j=Info->nOffset;j<Info->nOffset+190;j++)       {           dc->SetPixel(j,460-i,Info->clrRGB);       }    }    DeleteObject(dc);    return 0;}

运行效果:

采用CreateThread()创建多线程程序[通俗易懂]

单线程测试

采用CreateThread()创建多线程程序[通俗易懂]

多线程测试

工程源码下载地址:

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 = 0;i<3;i++)

    {

       hThead[i] = CreateThread(NULL,0,ThreadProc,&Info[i],0,&dwThreadID[i]);

    }

    //ReleaseDC(dc);

}

//线程执行函数

DWORD WINAPI ThreadProc(LPVOIDlpParam)

{

    threadInfo*Info = (threadInfo*)lpParam;

    for (int i=0;i<460;i++)

    {

       for (int j=Info->nOffset;j<Info->nOffset+190;j++)

       {

           Info->dc->SetPixel(j,460-i,Info->clrRGB);

       }

    }

    return 0;

}

运行结果:

采用CreateThread()创建多线程程序[通俗易懂]

为什么会这样呢?我还没有找到答案,望大家能给我个解释,谢谢。

===========================================================

4、多线程实例2:

该实例将演示一个简单的多线程协同工作的例子,以供大家学习和参考。大致原理是:5个人开始比赛(比如赛跑),谁先完成比赛就结束,并统比赛时间和赢者。主线程用于界面的相关显示,5个线程模拟5个人的行为(赛跑),另外一个线程用于检测5个线程的运行情况,只要有人到达终点,比赛就结束并做相关的技术统计。

主要函数为:

 

MulitThread_2Dlg.h : 头文件

//声明线程处理函数

DWORD WINAPI ThreadProc1(LPVOIDlpParam);

DWORD WINAPI ThreadProc2(LPVOIDlpParam);

//为了传递多个参数,我采用结构体

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 : 实现文件

//更新时间:毫秒

void CMulitThread_2Dlg::OnTimer(UINT_PTRnIDEvent)

{

    // TODO: 在此添加消息处理程序代码和/或调用默认值

    m_nTime+=100;//毫秒为单位

    CString str;

    str.Format(时间:%.1f秒”,m_nTime/1000.0);

    GetDlgItem(IDC_STATIC2)->SetWindowText(str);

    CDialog::OnTimer(nIDEvent);

}

 

//消息处理函数

LRESULT CMulitThread_2Dlg::OnGameOver(WPARAMwParam, LPARAMlParam)

{

    KillTimer(1);//关闭计时器

    if (wParam ==0)

    {
//
出错

       GetDlgItem(IDC_STATIC1)->SetWindowText(出错啦!”);

       GetDlgItem(IDC_STATIC2)->SetWindowText(“—“);

       AfxMessageBox(出错啦!”,MB_OK|MB_ICONERROR);

    }

    else

    {
//
成功

       //显示结果

       char *pName[] = {
张三”,李四”,王二”,小蔡”,赵干”};

       CStringstr;

       str.Format(赢者:%s”,pName[lParam]);

       GetDlgItem(IDC_STATIC1)->SetWindowText(str);

    }

    //使能开始按钮,以便可以开始下一次比赛

    GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE);

    return 0;

}

 

//开始比赛

void CMulitThread_2Dlg::OnBnClickedButton1()

{

    // TODO: 在此添加控件通知处理程序代码

    //使能开始按钮:无效

    GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);

    m_nTime =0;//初始化时间为

    CDC *dc = GetDC();

    CRect rt;

    GetClientRect(rt);

    dc->FillSolidRect(40,0,rt.Width()-49,rt.Height()-50,RGB(240,240,240));//刷新背景

    ReleaseDC(dc);

    //初始化线程的参数

    Info1[0].hWnd = Info1[1].hWnd = Info1[2].hWnd = Info1[3].hWnd = Info1[4].hWnd = GetSafeHwnd();

    Info1[0].nOffset = 0;Info1[1].nOffset = 90;Info1[2].nOffset = 180;Info1[3].nOffset = 270;Info1[4].nOffset = 360;

    //创建线程

    for (int i = 0;i<5;i++)

    {  

       m_hThead[i] = CreateThread(NULL,0,ThreadProc1,&Info1[i],CREATE_SUSPENDED,&m_dwThreadID[i]);   

    }

    SetTimer(1,100,NULL);//开始计时

    GetDlgItem(IDC_STATIC1)->SetWindowText(进行中…”);

    //开始运行

    for (int i = 0;i<5;i++)

    {  

       ResumeThread(m_hThead[i]); 

    }

    //开始运行监测结果线程

    Info2.hWnd = m_hWnd;

    Info2.phHandle = m_hThead;

    hThead = CreateThread(NULL,0,ThreadProc2,&Info2,0,NULL);

}

 

//比赛线程

DWORD WINAPI ThreadProc1(LPVOIDlpParam)

{

    threadInfo1*info = (threadInfo1*)lpParam;

    CDC *dc = CWnd::FromHandle(info->hWnd)->GetDC();

    for (int i=40;i<570;i+=2)

    {

       for (int j=0;j<1000;j++)

       {
//
重复操作,以便人眼观察

           dc->Rectangle(CRect(i,info->nOffset,i+1,info->nOffset+80));

       }

    }

    DeleteObject(dc);

    return 0;

}

 

//监视线程:谁先完成比赛就结束

DWORD WINAPI ThreadProc2(LPVOIDlpParam)

{

    threadInfo2*info = (threadInfo2*)lpParam;

    DWORD dwRet = 0;

    //等待个线程中的一个完成

    dwRet = WaitForMultipleObjects(5,info->phHandle,FALSE,INFINITE);

    if (dwRet == WAIT_FAILED)

    {
//
出错啦

       ::SendMessage(info->hWnd,WM_GAMEOVER,0,0);

       return 0;

    }

    //终止各个线程

    for (int i=0;i<5;i++)

    {

       TerminateThread(info->phHandle[i],0);

    }

    //发送比赛结果消息

    ::SendMessage(info->hWnd,WM_GAMEOVER,1,dwRetWAIT_OBJECT_0);

    return 0;

}

 

运行结果:

采用CreateThread()创建多线程程序[通俗易懂]

工程源码下载地址:

http://download.csdn.net/detail/cbnotes/4867333

欢迎大家修改和指正。

注意事项:

1.    该程序连主线程一共7个线程。其中一个线程专门用于检测5个比赛线程的运行结果检测,为什么要专门开这个线程而不在主线程中进行呢?主要是WaitForMultipleObjects()函数是一个阻塞函数,如果在主线程中运行该函数,将使整个程序的界面不能操作(如:不能移动窗口等),因为一直阻塞在WaitForMultipleObjects函数处,而不能处理其它消息,不信大家可以试试。

2.    线程同步的两个比较重要的函数为WaitForSingleObject()和WaitForMultipleObjects(),具体使用请参考MSDN。这两个函数都是阻塞函数,一直等待授信的对象发生才返回。

3.    采用消息的方式通知主线程的运行结果,该方法比较简单有效。一般的多线程程序都是采用主线程负责显示,辅助线程来完成比较耗时的任务,等任务完成后再通知主线程运行结果。

 

转载请说明出处,谢谢。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/157313.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • matlab流场可视化后处理「建议收藏」

    matlab流场可视化后处理「建议收藏」matlab流场可视化后处理1流体中标量的可视化1.1云图1.2切片图绘制1.3三维等值面图绘制2流体中矢量的可视化2.1箭头图或速度图2.2流线图2.4带节点的流线图2.5流管图和流带图2.6圆锥体图2.7粒子动画图3参考链接1流体中标量的可视化流体力学中常见的标量为位置、速度绝对值、压强等。1.1云图常用的云图绘制有pcolor、image、imagesc、imsh…

    2022年10月31日
  • 【js】Input事件

    【js】Input事件InputEvent常用事件触发的先后顺序如下:1keydown2keypress3textInput4input5keyupkeydown,keyup1全部浏览器支持2当用户按下/释放键盘上的任意键时触发3event.keyCode,返回键盘上按键对应的特定键码(兼容性:分号在Firefox,Opera上返回的是ASC

  • 安装程序在计算机中识别出下列大容量存储设备「建议收藏」

    安装程序在计算机中识别出下列大容量存储设备「建议收藏」今天使用USB安装2003在一台lenovo笔记本发现提示安装程序在计算机中识别出下列大容量存储设备解决方案:BIOS硬盘更改为兼容oride即可 转载于:https://blog.51cto.com/vn98z/905299…

  • egg yolk_人类蛋白数据库

    egg yolk_人类蛋白数据库欢迎关注”生信修炼手册”!直系同源蛋白的预测在系统发育,比较基因组学等多个领域都占用重要地位,COG数据库开创了同源蛋白数据库的先河,后续又不断有新的数据库涌现,而eggNOG就是目前使…

    2022年10月29日
  • Redis设置过期时间_redis过期时间原理

    Redis设置过期时间_redis过期时间原理varredis=require(‘redis’),RDS_PORT=6389,//端口号RDS_HOST=’127.0.0.1′,//服务器IPRDS_PWD=’88888888888888′,//密码RDS_OPTS={},//设置项rclient=redi…

  • executescalar mysql_ExecuteScalar

    executescalar mysql_ExecuteScalar这两个答案和一点点思考使我想到了一个接近答案的东西。首先再澄清一下:该应用程序是用C#(2.0+)编写的,并使用ADO.NET与SQLServer2005进行通信。镜像设置是托管主体和镜像的两个W2k3服务器以及托管作为监视器的快速实例的第三个服务器。这样做的好处是,故障转移对于使用数据库的应用程序几乎是透明的,它将对某些连接引发错误,但从根本上讲一切都会很好地进行。是的,我们得到了奇怪的误报…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号