Win32 多线程类
前些时候用 CUDA 做了个加速三维配准的程序,做的结果很是让人郁闷,在一个四核 CPU 和 9600GT 显卡上运行的速度差不多,偏偏对方又规定了不能换更高档的显卡,于是决定改用 CPU 多线程来做并行计算,结果还不错,于是把程序写成了 win32 多线程的类,封装在 DLL 中。下面介绍一下怎么写多线程的类,如果有错误的话希望大家指正。
首先是关于 Win32 多线程的一些基本知识,包括线程创建和同步等,更详细的内容大家可以在清华大学出版社的《多核程序设计》中找到。对多线程编程有过初步了解的可以直接跳过这一部分。
Win32 API 中用于创建线程的主要有两个函数,一个是定义在 Windows.h 中的 CreateThread ,另外一个是定义在 process.h 中的 _beginthread 函数。这里我用的是形式比较简单的 _beginthread 函数。
线程同步主要以下面几种方式实现:全局变量,事件,临界区,互斥量和信号量,采用哪种方式取决于要用多线程做什么。例如用事件做线程同步的话,需要调用 CreateEvent 和 WaitForSingleObject 两个函数。
另外,还可以用 SetThreadPriority 设置线程的优先级,用 SuspendThread 和 ResumeThread 挂起和恢复线程,用 WaitForSingleObject 和 WaitForMultipleObject 实现线程等待,用 ExitThread 和 TerminateThread 强制终止线程。
在了解了多线程编程以后,就可以继续讲封装了多线程的类的编写了。
首先新建一个空的 Win32 控制台应用程序,用类向导在其中添加一个名为 CMThread 的类,类的代码如下:
//MThread.h
#pragma once
class CMThread
{
public :
CMThread (void );
public :
~CMThread (void );
public :
void Run (); // 创建线程
private :
int a ;
int b ;
private :
void ThreadA (LPVOID pParam ); // 线程A 的线程函数
void ThreadB (LPVOID pParam ); // 线程B 的线程函数
};
//MThread.cpp
#include "MThread.h"
#include <iostream>
#include <process.h>
using namespace std ;
CMThread ::CMThread (void )
{
a = 0;
b = 1;
}
CMThread ::~CMThread (void )
{
}
void CMThread ::ThreadA (LPVOID pParam )
{
cout <<" 线程" <<a <<" 调用成功" <<endl ;
}
void CMThread ::ThreadB (LPVOID pParam )
{
cout <<" 线程" <<b <<" 调用成功" <<endl ;
}
// 创建线程
void CMThread ::Run ()
{
_beginthread (ThreadA , 0, NULL );
_beginthread (ThreadB , 0, NULL );
}
为了测试多线程类,还需要添加一个 main.cpp 文件:
//main.cpp
#include "MThread.h"
#include <iostream>
void main ()
{
CMThread mtThread ;
mtThread .Run ();
}
编译程序,会出现如下错误:
error C3867: 'CMThread::ThreadA': function call missing argument list; use '&CMThread::ThreadA' to create a pointer to member
error C3867: 'CMThread::ThreadB': function call missing argument list; use '&CMThread::ThreadB' to create a pointer to member
双击错误,会发现是 _beginthread(ThreadA, 0, NULL) 出错,如果只是从字面上理解,似乎是因为函数指针调用方式错误引起的,但是将 ThreadA 换成 &CMThread::ThreadA 时仍然不能运行成功。其实这是因为线程函数必须是全局函数。为此需要将线程声明为 static 函数。这时候又会出现一个新的问题: static 类型的函数中不能访问类中其它的非静态成员 ! 解决这个问题的一种方法是将线程函数中用到的类成员函数和变量都声明为 static 型,但是这种方法显然不实用。另外一种方法是将类本身作为参数传递到线程函数中,为此,需要对线程函数和 Run 函数作如下修改:
void CMThread ::ThreadA (LPVOID pParam )
{
CMThread *mt = (CMThread *)pParam ;
cout <<" 线程" <<mt ->a <<" 调用成功" <<endl ;
}
void CMThread ::ThreadB (LPVOID pParam )
{
CMThread *mt = (CMThread *)pParam ;
cout <<" 线程" <<mt ->b <<" 调用成功" <<endl ;
}
// 创建线程
void CMThread ::Run ()
{
_beginthread (ThreadA , 0, this );
_beginthread (ThreadB , 0, this );
}
这个时候再运行程序就不会报错了,但是多运行几次就会发现,每次运行输出的结果只有一半或是完全没有输出 ( 如下图所示 ) 。原来是线程没有同步。
加上线程同步后的代码如下:
//MThread.h
#pragma once
#include <Windows.h>
#include <process.h>
class CMThread
{
public :
CMThread (void );
public :
~CMThread (void );
public :
void Run (); // 创建线程
private :
int a ;
int b ;
HANDLE evThreadAOver ; // 用于标识线程A 结束的事件
HANDLE evThreadBOver ; // 用于标识线程B 结束的事件
private :
static void ThreadA (LPVOID pParam ); // 线程A 的线程函数
static void ThreadB (LPVOID pParam ); // 线程B 的线程函数
};
//MThread.cpp
#include "MThread.h"
#include <iostream>
using namespace std ;
CMThread ::CMThread (void )
{
a = 0;
b = 1;
}
CMThread ::~CMThread (void )
{
}
void CMThread ::ThreadA (LPVOID pParam )
{
CMThread *mt = (CMThread *)pParam ;
cout <<" 线程" <<mt ->a <<" 调用成功" <<endl ;
SetEvent (mt ->evThreadAOver );
}
void CMThread ::ThreadB (LPVOID pParam )
{
CMThread *mt = (CMThread *)pParam ;
cout <<" 线程" <<mt ->b <<" 调用成功" <<endl ;
SetEvent (mt ->evThreadBOver );
}
// 创建线程
void CMThread ::Run ()
{
// 产生事件
evThreadAOver = CreateEvent (NULL , FALSE , FALSE , NULL );
evThreadBOver = CreateEvent (NULL , FALSE , FALSE , NULL );
// 创建线程A 和B
_beginthread (ThreadA , 0, this );
_beginthread (ThreadB , 0, this );
// 等待线程A 和B 结束
WaitForSingleObject (evThreadAOver , INFINITE );
WaitForSingleObject (evThreadBOver , INFINITE );
}
//main.cpp
#include "MThread.h"
#include <iostream>
void main ()
{
CMThread mtThread ;
mtThread .Run ();
}
运行结果如图所示