多线程的简介
线程---操作系统调度的最小单位。线程包含在进程中,是进程中实际运行的单位。一个进程中可以同时运行多个线程,每个线程可以执行不同的任务,这就是所谓的多线程。同一进程中的多个线程将共享该进程中的全部系统资源,如虚拟地址空间、文件描述符和信号处理等,但是同一个进程中的多个线程都有各自的调用栈、寄存器环境和线程本地存储。
对于单核(单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函数中,创建了一个对话框。
运行结果:
|