2010年9月27日星期一

Windows多线程编程

前面写了一篇Linux多线程编程,由于算法要整合到一个运行在Windows平台下的系统中,所以需要改成Windows下的多线程方式(看我折腾的)。这里简单列一个Windows多线程编程的例子。其实我也想尝试是用一个跨平台的C++库BoostBoost::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函数亦可终止线程。

BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);

该函数用来结束由hThread参数指定的线程, 并把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实现多线程

MFC对Windows API进行了封装,可以说使用起来更加简单,而且提供了很多多线程的扩张功能。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


没有评论:

发表评论