前面写了一篇Linux多线程编程,由于算法要整合到一个运行在Windows平台下的系统中,所以需要改成Windows下的多线程方式(看我折腾的)。这里简单列一个Windows多线程编程的例子。其实我也想尝试是用一个跨平台的C++库Boost,Boost::Thread类库可以在不同的平台下运行多线程,而且多线程功能非常丰富和强大,使用起来也很容易,但是还需要安装这个库,先暂时放弃这个方案,因为毕竟我现在需要的多线程功能非常简单。
Windows API实现多线程
在上一篇文章中,已经列出了一个表,列出了Linux线程API和Windows下线程API的大致对应关系。根据那个表,和Linux下多线程经验,也能大概知道Windows下实现多线程的方法首先来看一个例子:
/* example.c */
#include <stdfx.h>
#include <stdio.h>
#include <process.h>const int NLOOP = 100;
int counter = 0;
void thread(void*);
CRITICAL_SECTION beswap ;
int main()
{
HANDLE pnt[2];
InitializeCriticalSection(&beswap);
pnt[0] = (HANDLE)_beginthread(doit,0,NULL);
pnt[1] = (HANDLE)_beginthread(doit,0,NULL);
WaitForMultipleObjects( 2, pnt, TRUE, 1000L);
DeleteCriticalSection(&beswap);
return 0;
}
void doit(void*)
{
printf("go...\n");
int i, val = 0;
for(i = 0; i < NLOOP; ++i)
{
EnterCriticalSection(&beswap);
val = counter;
printf("%d\n",val+1);
counter = val + 1;
LeaveCriticalSection(&beswap);
}
printf("end...\n");
return ;
}
根据这个例子,多线程的过程解释如下:
1、编写线程函数,这里线程函数要遵循如下函数原型:
DWORD WINAPI threadFunc(LPVOID lpvThreadParm);
函数的输入参数是一个DWORD的类型,可以是一个整数,也可以是一个内存指针,具体的意义由编程者自己决定。返回值是一个DWORD型的值。
2、创建一个线程
一个进程的主线程是由操作系统自动生成,如果你要让一个主线程创建额外的线程,你可以调用来CreateThread完成,使用的时候注意包含#include<process.h>头文件。其函数原型如下:
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpsa,DWORD cbstack,LPTHREAD_START_ROUTINE lpStartAddr,
LPVOID lpvThreadParm, DWORD fdwCreate,LPDWORD lpIDThread);
其中,lpsa参数为一个指向SECURITY_ATTRIBUTES结构的指针。如果想让对象为缺省安全属性的话,可以传一个NULL,如果想让任一个子进程都可继承一个该线程对象句柄,必须指定一个SECURITY_ATTRIBUTES结构,其中bInheritHandle成员初始化为TRUE。
参数cbstack表示线程为自己所用堆栈分配的地址空间大小,0表示采用系统缺省值。
参数lpStartAddr用来表示新线程开始执行时代码所在函数的地址,即为线程函数。
lpvThreadParm为传入线程函数的参数,
fdwCreate参数指定控制线程创建的附加标志,可以取两种值。如果该参数为0,线程就会立即开始执行,如果该参数为CREATE_SUSPENDED,则系统产生线程后,初始化CPU,登记CONTEXT结构的成员,准备好执行该线程函数中的第一条指令,但并不马上执行,而是挂起该线程。
最后一个参数lpIDThread 是一个DWORD类型地址,返回赋给该新线程的ID值。
此外,还可以使用_beginthread等函数来创建线程,正如本例子中使用的方法:
handle=(HANDLE)_beginthread(threadFunc,0, NULL);
函数_beginthread的函数原型如下:
uintptr_t _beginthread(
void( *start_address )( void * ),
unsigned stack_size,
void *arglist
);
start_address新线程的起始地址,指向新线程调用的函数的起始地址;stack_size stack_size 新线程的堆栈大小,可以为0;arglist arglist 传递给线程的参数列表,无参数是为NULL。
3、终止线程,如果某线程调用了ExitThread 函数,就可以终止自己。
VOID ExitThread(UINTfuExitCode );
这个函数为调用该函数的线程设置了退出码fuExitCode后, 就终止该线程。调用TerminateThread函数亦可终止线程。
该函数用来结束由hThread参数指定的线程, 并把dwExitCode设成该线程的退出码。当某个线程不在响应时,我们可以用其他线程调用该函数来终止这个不响应的线程。BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);
4、另外还有设置程序的优先级,挂起和恢复线程之类的。这里就不详述,因为还没有用到。
BOOL SetThreadPriority(HANDLE hThread,intnPriority); // 设置线程优先级
DWORD ResumeThread(HANDLEhThread); // 恢复线程
DWORD SuspendThread(HANDLE hThread); // 挂起线程
5、这里还用到了线程的阻塞函数WaitForMultipleObjects,原型:
DWORD WaitForMultipleObjects(
DWORD nCount,
const HANDLE* lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds
);
WaitForMultipleObjects是Windows中的一个功能非常强大的函数,几乎可以等待Windows中的所有的内核对象。当WaitForMultipleObjects等到多个内核对象的时候,如果它的bWaitAll 参数设置为false。其返回值减去WAIT_OBJECT_0 就是参数lpHandles数组的序号。如果同时有多个内核对象被触发,这个函数返回的只是其中序号最小的那个。如果为TRUE 则等待所有信号量有效在往下执行。(FALSE 当有其中一个信号量有效时就向下执行)。
对应的还有一个函数WaitForSingleObject,用来等待指定的内核对象。
6、还有互斥锁的使用方法,在例子中也有很清楚的使用方法。单进程的线程可以使用这种临界资源对象来解决同步互斥问题,该对象不能保证哪个个线程能够获得到临界资源对象,因而该系统能公平的对待每一个线程。
MFC实现多线程
CWinThread* AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);
CWinThread* AfxBeginThread(
CRuntimeClass* pThreadClass,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);
这是一个重载函数,前一个用来创建一个工作线程,后一个用来创建界面线程。MFC框架还提供了很多同步类,来实现线程间的同步和通信,具体的使用方法可以参考MSDN,由于还没有用到,等用到了,再详细总结。另外,在编程中,我也是用WaitForMultipleObjects来等待了AfxBeginThread创建的线程,而没有出现问题。
参考地址:
http://wujblog.appspot.com/2010/09/26/linux-multi-thread.html
http://www.wangchao.net.cn/bbsdetail_69676.html
http://blog.csdn.net/zjl_1026_2001/archive/2008/03/18/2193626.aspx
http://msdn.microsoft.com/en-us/library/s3w9x78e(VS.80).aspx
http://msdn.microsoft.com/zh-cn/library/975t8ks0(v=VS.80).aspx