您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
c++多线程
 
作者:richerg85 来源:CSDN 发布于:2015-6-17
  3458  次浏览      18
 

多线程的简介

线程---操作系统调度的最小单位。线程包含在进程中,是进程中实际运行的单位。一个进程中可以同时运行多个线程,每个线程可以执行不同的任务,这就是所谓的多线程。同一进程中的多个线程将共享该进程中的全部系统资源,如虚拟地址空间、文件描述符和信号处理等,但是同一个进程中的多个线程都有各自的调用栈、寄存器环境和线程本地存储。

对于单核(单CPU)系统来说,即便处理器一次只能运行一个线程,但是操作系统通过时间片轮转技术,在不同的线程之间进行切换,让用户产生可以同时处理多个任务的错觉,这样的程序运行机制称为软件的多线程。

对于多核(多个CPU)系统来说,这样的系统能同时进行真正的多线程多任务处理。这种运行机制可以称为硬件的多线程技术。

多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:

1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。

2) 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。

3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。

多线程编程实例1

说明:本系列所有的实例都是在vc6.0下实现的,并且都是基于MFC AppWizard[exe]工程创建的“Dialog based”应用程序。

实例1,简单的多线程,实现动态显示时间

工程名称为Mthread1,首先在Mthread1Dlg.h中声明线程函数---void ThreadProc(),此函数为全局函数。

部分代码如下:

// Mthread1Dlg.h : header file  
//
... ...

void ThreadProc();//线程函数声明
class CMthread1Dlg : public CDialog
{
... ...
protected:
HICON m_hIcon;
HANDLE hThread;//线程句柄
... ...

DECLARE_MESSAGE_MAP()
};

MthreadDlg.cpp

// Mthread1Dlg.cpp : implementation file  
//

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
volatile BOOL m_bRun;//代表线程是否正常运行

void ThreadProc() //线程函数
{
CTime time;
CString strTime;
m_bRun = TRUE;

while(m_bRun)
{
time = CTime::GetCurrentTime();
strTime = time.Format("%H:%M:%S");

::SetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_TIME,strTime);
Sleep(1000);
}
}
class CAboutDlg : public CDialog
{
... ...
void CMthread1Dlg::OnStart()
{
// TODO: Add your control notification handler code here
hThread = CreateThread(
NULL, // SD
0, // initial stack size
(LPTHREAD_START_ROUTINE)ThreadProc, // thread function
NULL, // thread argument
0, // creation option
&threadID // thread identifier
);
GetDlgItem(IDC_START)->EnableWindow(FALSE);
GetDlgItem(IDC_STOP)->EnableWindow(TRUE);
}

void CMthread1Dlg::OnStop()
{
// TODO: Add your control notification handler code here
m_bRun = FALSE;
GetDlgItem(IDC_START)->EnableWindow(TRUE);
GetDlgItem(IDC_STOP)->EnableWindow(FALSE);
}

执行结果:

相应函数API说明

CreateThread

函数原型:

HANDLE CreateThread(  
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
DWORD dwStackSize, // initial stack size
LPTHREAD_START_ROUTINE lpStartAddress, // thread function
LPVOID lpParameter, // thread argument
DWORD dwCreationFlags, // creation option
LPDWORD lpThreadId // thread identifier
);

此函数在其调用进程的进程空间里创建一个线程。

参数说明:

lpThreadAttributes:指向SECURITY_ATTRIBUTES结构体,该结构体决定了函数返回句柄是否被子进线程继承。

如果为NULL,则不能被继承。

dwStackSize:指定了线程的堆栈大小,一般为0,使用默认的堆栈大小。

lpStartAddress:指向应用定义的线程函数,线程函数类型为LPTHREAD_START_ROUTINE。此值代表线程的开始地址。

lpParameter:线程函数所带的参数。是一个指向结构的指针,不需传递参数时,为NULL。

dwCreateFlags:线程标志。如果指定为CREATE_SUSPENDED,线程创建的时候的状态为挂起状态,

线程不会立即执行直到调用ResumeThread函数。

如果值为0,线程会立即执行。

lpThreadId:保存新线程的id.

多线程实例二

此实例演示采用CreateThread函数在主线程中创建一个线程,并且向创建的线程中传递一个参数。

由于采用MFC编程,自动生成的代码比较多,还是列出部分实现多线程的代码。

线程函数:

void ThreadProc(int count)  
{
for (int i=0; i < count; i++)
{
Beep(2000,50);
Sleep(200);
}
}

主线程函数:

void CMthread2Dlg::OnStart()   
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);//从控件中检索数据
int count = m_count;


hThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)ThreadProc,
(VOID *)count,
0,
&threadID
);


GetDlgItem(IDC_START)->EnableWindow(FALSE);
WaitForSingleObject(hThread,INFINITE); //当线程挂起时,为有信号状态
GetDlgItem(IDC_START)->EnableWindow(TRUE);
}

注:变量m_count和控件IDC_COUNT做了关联。

函数说明:

BOOL UpdateData( BOOL bSaveAndValidate = TRUE );

MFC中的窗口函数,在对话框中,当建立控件和变量之间的关联关系后,修改变量值,希望对话框更新显示,则bSaveAndValidate=FALSE,即调用UpdateData(FALSE);当需要获取对话框中控件输入的值时,则bSaveAndValidate=TRUE,即调用UpdateData(TRUE)。

DWORD WaitForSingleObject(
HANDLE hHandle, // handle to object
DWORD dwMilliseconds // time-out interval
);

此函数的详细描述参见:http://blog.csdn.net/richerg85/article/details/7354154

注意:

主线程中,调用了WaitForSingleObject函数,此函数的作用是监视hHandle的状态,当监视的句柄为有信号状态时,即此对象为空闲状态时,此函数返回,才能执行其后的代码。

在此处,用WaitForSingleObject作用:

由于c++主程序终止,同时它创建的相应的线程也会终止,它不管子线程是否执行完成,因此,上文中如果不调用WaitForSingleObject函数,则子线程ThreadProc可能没有执行完或者没执行。

此程序执行结果图:

多线程实例三

此实例演示多线程中,主线程向子线程传递一个结构体。

在头文件中,声明线程函数及结构体:

UINT ThreadProc(LPVOID lpParam);  
struct threadInfo
{
UINT nMilliSecond;
CProgressCtrl *pctrProcess;
};

子线程定义函数

threadInfo myInfo;  
UINT ThreadProc(LPVOID lpParam)
{
threadInfo *pInfo = (threadInfo *)lpParam;
for (int i=0; i<100; i++)
{
int iTmp = pInfo->nMilliSecond;
pInfo->pctrProcess->SetPos(i);
Sleep(iTmp);
}
return 0;
}

主线程调用子线程函数:

void CMthread3Dlg::OnStart()   
{
// TODO: Add your control notification handler code here
UpdateData();//默认为TRUE
myInfo.nMilliSecond = m_nMillSecond;
myInfo.pctrProcess = &m_ctrProcess;

hThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)ThreadProc,
&myInfo,
0,
&threadID
);
/*GetDlgItem(IDC_START)->EnableWindow(FALSE);
WaitForSingleObject(hThread,INFINITE);
GetDlgItem(IDC_START)->EnableWindow(TRUE);*/
}

注意注释的部分,如果调用WaitForSingleObject函数,此程序会出现死锁。因为对话框中有个进度条,子线程中设置进度条的进度,但是进度条的刷新需要主线程来完成,当主线程调用WaitForSingleObject函数挂起后,子线程设置正在设置进度条,一直等待主线程将刷新消息出来完毕返回才检测通知事件。这样两个线程一直处于相互等待,出现死锁。

程序执行结果:

多线程实例4

此实例演示你的机器最多能创建多少个线程。此实例程序相当简单。

线程函数:

volatile BOOL m_bRUN = TRUE; //表示是否能继续添加线程  
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
while (m_bRUN)
{
Sleep(2000);
}
return 0;
}

主线程函数:

void CMthread4Dlg::OnStart()   
{
// TODO: Add your control notification handler code here
DWORD threadID;
long nCount = 0;
m_nCount = 0;
UpdateData(FALSE);
GetDlgItem(IDC_START)->EnableWindow(FALSE);


while (m_bRUN)
{
if (CreateThread(NULL,0,ThreadProc,NULL,0,&threadID) == NULL)
{
m_bRUN = FALSE;
break;
}
else
{
nCount++;
}
}
m_nCount = nCount;
UpdateData(FALSE);
Sleep(5000);
GetDlgItem(IDC_START)->EnableWindow(TRUE);
m_bRUN = TRUE;
}

说明:当m_bRUN一直为TRUE的时候,程序一直创建线程,直至创建的线程到达最大值。

执行结果:

多线程实例五

此实例演示,创建了一个线程类,线程类继承自CWinThread。主线程通过AfxBeginThread函数调用线程类。

主线程调用线程类:

void CMthread6Dlg::OnUiThread()   
{
// TODO: Add your control notification handler code here
CWinThread *pThread = AfxBeginThread(RUNTIME_CLASS(CUIThread));
}

线程类:

头文件中类定义:

class CMthread6Dlg : public CDialog

线程类:

// UIThread.cpp : implementation file  
//

#include "stdafx.h"
#include "Mthread6.h"
#include "UIThread.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CUIThread

IMPLEMENT_DYNCREATE(CUIThread, CWinThread)

CUIThread::CUIThread()
{
}

CUIThread::~CUIThread()
{
}

BOOL CUIThread::InitInstance()
{
// TODO: perform and per-thread initialization here
m_dlg.Create(IDD_UITHREADDLG);
m_dlg.ShowWindow(SW_SHOW);
m_pMainWnd = &m_dlg;
return TRUE;
}

int CUIThread::ExitInstance()
{
// TODO: perform any per-thread cleanup here
m_dlg.DestroyWindow();
return CWinThread::ExitInstance();
}

BEGIN_MESSAGE_MAP(CUIThread, CWinThread)
//{{AFX_MSG_MAP(CUIThread)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CUIThread message handlers

从上边的代码中,自建的线程类CUIThread重写了CWinThread类的InitInstance和ExitInstance函数

InitInstance函数中,创建了一个对话框。

运行结果:

   
3458 次浏览       18
相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程
最新课程计划
信息架构建模(基于UML+EA)3-21[北京]
软件架构设计师 3-21[北京]
图数据库与知识图谱 3-25[北京]
业务架构设计 4-11[北京]
SysML和EA系统设计与建模 4-22[北京]
DoDAF规范、模型与实例 5-23[北京]
Visual C++编程命名规则
任何时候都适用的20个C++技巧
C语言进阶
串口驱动分析
轻轻松松从C一路走到C++
C++编程思想
更多...   


C++并发处理+单元测试
C++程序开发
C++高级编程
C/C++开发
C++设计模式
C/C++单元测试


北京 嵌入式C高质量编程
中国航空 嵌入式C高质量编程
华为 C++高级编程
北京 C++高级编程
丹佛斯 C++高级编程
北大方正 C语言单元测试
罗克韦尔 C++单元测试
更多...