热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

本位转自:http:blog.csdn.netcbnotesarticledetails8277180在window环境下,Win32提供了一系列的AP


本位转自:http://blog.csdn.net/cbnotes/article/details/8277180

在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 toLPTHREAD_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)为了演示效果,采用了比较耗时的打点处理。

主要的函数如下:

在头文件的定义

[cpp] view plaincopy
print?
  1. //线程函数声明  
  2. DWORD WINAPI ThreadProc(LPVOIDlpParam);  
  3. //为了传递多个参数,我采用结构体  
  4. struct threadInfo  
  5. {  
  6.     HWND hWnd;       //窗口句柄  
  7.     int  nOffset;    //偏移量  
  8.     COLORREF clrRGB; //颜色  
  9. };  
  10.    
  11. protected:  
  12. HANDLE hThead[3];    //用于存储线程句柄  
  13.     DWORD  dwThreadID[3];//用于存储线程的ID  
  14.   threadInfo Info[3];   //传递给线程处理函数的参数  


//实现文件中

[cpp] view plaincopy
print?
  1. //单线程测试  
  2. void CMultiThread_1Dlg::OnBnClickedButton1()  
  3. {  
  4.     // TODO: 在此添加控件通知处理程序代码  
  5.     //使能按钮  
  6.     GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);  
  7.     GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE);  
  8.     CDC *dc = GetDC();  
  9.     CRect rt;  
  10.     GetClientRect(rt);  
  11.     dc->FillSolidRect(0,0,rt.Width(),rt.Height()-70,RGB(240,240,240));//刷新背景  
  12.     dc->TextOut(97,470,"#1");  
  13.     dc->TextOut(297,470,"#2");  
  14.     dc->TextOut(497,470,"#3");  
  15.     //#1  
  16.     for (int i&#61;0;i<460;i&#43;&#43;)  
  17.     {  
  18.        for (int j &#61;10 ;j<200;j&#43;&#43;)  
  19.        {  
  20.            dc->SetPixel(j,460-i,RGB(255,0,0));  
  21.        }  
  22.     }  
  23.     //#2  
  24.     for (int i&#61;0;i<460;i&#43;&#43;)  
  25.     {  
  26.        for (int j &#61;210 ;j<400;j&#43;&#43;)  
  27.         {  
  28.            dc->SetPixel(j,460-i,RGB(0,255,0));  
  29.        }  
  30.     }  
  31.     //#3  
  32.     for (int i&#61;0;i<460;i&#43;&#43;)  
  33.     {  
  34.        for (int j &#61;410 ;j<600;j&#43;&#43;)  
  35.        {  
  36.            dc->SetPixel(j,460-i,RGB(0,0,255));  
  37.        }  
  38.     }  
  39.     ReleaseDC(dc);  
  40.     //使能按钮  
  41.     GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE);  
  42.     GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE);  
  43. }  
  44.    
  45. //多线程测试  
  46. void CMultiThread_1Dlg::OnBnClickedButton2()  
  47. {  
  48.     // TODO: 在此添加控件通知处理程序代码  
  49.     CDC *dc &#61; GetDC();  
  50.     CRect rt;  
  51.     GetClientRect(rt);  
  52.     dc->FillSolidRect(0,0,rt.Width(),rt.Height()-70,RGB(240,240,240));//刷新背景  
  53.     dc->TextOut(97,470,"#1");  
  54.     dc->TextOut(297,470,"#2");  
  55.     dc->TextOut(497,470,"#3");  
  56.     //初始化线程的参数  
  57.     Info[0].hWnd &#61; Info[1].hWnd &#61; Info[2].hWnd &#61; GetSafeHwnd();  
  58.     Info[0].nOffset &#61; 10;Info[1].nOffset &#61; 210;Info[2].nOffset &#61; 410;  
  59.     Info[0].clrRGB &#61; RGB(255,0,0);Info[1].clrRGB&#61; RGB(0,255,0);Info[2].clrRGB &#61; RGB(0,0,255);  
  60.     //创建线程  
  61.     for (int i &#61; 0;i<3;i&#43;&#43;)  
  62.     {  
  63.        hThead[i] &#61; CreateThread(NULL,0,ThreadProc,&Info[i],0,&dwThreadID[i]);  
  64.     }  
  65.     ReleaseDC(dc);  
  66. }  
  67.    
  68. DWORD WINAPI ThreadProc(LPVOIDlpParam)  
  69. {  
  70.     threadInfo*Info &#61; (threadInfo*)lpParam;  
  71.     CDC *dc &#61; CWnd::FromHandle(Info->hWnd)->GetDC();  
  72.     for (int i&#61;0;i<460;i&#43;&#43;)  
  73.     {  
  74.        for (int j&#61;Info->nOffset;jnOffset&#43;190;j&#43;&#43;)  
  75.        {  
  76.            dc->SetPixel(j,460-i,Info->clrRGB);  
  77.        }  
  78.     }  
  79.     DeleteObject(dc);  
  80.     return 0;  
  81. }  


运行效果&#xff1a;

单线程测试


多线程测试

工程源码下载地址&#xff1a;

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

欢迎大家修改和指正。

注意事项&#xff1a;

&#xff08;1&#xff09;传递给线程执行函数的参数不能是局部变量&#xff0c;而且必须是参数的地址。如&#xff1a;

Int nOffset &#61; 10;

CreateThread(NULL,0,ThreadProc,nOffset,0,&dwThreadID[i]);//错误

CreateThread(NULL,0,ThreadProc,&nOffset,0,&dwThreadID[i]);//错误

Int *pOffset &#61; newint(10);

CreateThread(NULL,0,ThreadProc,pOffset,0,&dwThreadID[i]);//正确

&#xff08;2&#xff09;线程执行函数必须是全局函数。

&#xff08;3&#xff09;请大家改改下面的程序&#xff0c;且解释下为什么&#xff1f;

这是我开始写程序遇到的一个问题&#xff0c;

改写上面的函数&#xff1a;只是将结构体中一个参数改为CDC指针&#xff0c;以便直接调用。

struct threadInfo

{

    CDC * dc;        //画布

    int  nOffset;    //偏移量

    COLORREF clrRGB//颜色

};

//多线程测试

void CMultiThread_1Dlg::OnBnClickedButton2()

{

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

    CDC *dc &#61; 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 &#61; Info[1]dc &#61; Info[2].dc &#61; dc;

    Info[0].nOffset &#61; 10;Info[1].nOffset &#61; 210;Info[2].nOffset &#61; 410;

    Info[0].clrRGB &#61; RGB(255,0,0);Info[1].clrRGB&#61; RGB(0,255,0);Info[2].clrRGB &#61; 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;

4、多线程实例2&#xff1a;

该实例将演示一个简单的多线程协同工作的例子&#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(WPARAMwParamLPARAMlParam)

{

    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,dwRetWAIT_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;谢谢。





2


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • MPLS VP恩 后门链路shamlink实验及配置步骤
    本文介绍了MPLS VP恩 后门链路shamlink的实验步骤及配置过程,包括拓扑、CE1、PE1、P1、P2、PE2和CE2的配置。详细讲解了shamlink实验的目的和操作步骤,帮助读者理解和实践该技术。 ... [详细]
  • NotSupportedException无法将类型“System.DateTime”强制转换为类型“System.Object”
    本文介绍了在使用LINQ to Entities时出现的NotSupportedException异常,该异常是由于无法将类型“System.DateTime”强制转换为类型“System.Object”所导致的。同时还介绍了相关的错误信息和解决方法。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 本文介绍了Java集合库的使用方法,包括如何方便地重复使用集合以及下溯造型的应用。通过使用集合库,可以方便地取用各种集合,并将其插入到自己的程序中。为了使集合能够重复使用,Java提供了一种通用类型,即Object类型。通过添加指向集合的对象句柄,可以实现对集合的重复使用。然而,由于集合只能容纳Object类型,当向集合中添加对象句柄时,会丢失其身份或标识信息。为了恢复其本来面貌,可以使用下溯造型。本文还介绍了Java 1.2集合库的特点和优势。 ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文由编程笔记#小编整理,主要介绍了关于数论相关的知识,包括数论的算法和百度百科的链接。文章还介绍了欧几里得算法、辗转相除法、gcd、lcm和扩展欧几里得算法的使用方法。此外,文章还提到了数论在求解不定方程、模线性方程和乘法逆元方面的应用。摘要长度:184字。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 合并列值-合并为一列问题需求:createtabletab(Aint,Bint,Cint)inserttabselect1,2,3unionallsel ... [详细]
  • 1Lock与ReadWriteLock1.1LockpublicinterfaceLock{voidlock();voidlockInterruptibl ... [详细]
author-avatar
jimscloudy
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有