ACE的线程管理机制

来源:互联网 发布:java小程序抽签源代码 编辑:程序博客网 时间:2024/05/22 04:40


一、线程的创建与管理

不同的操作系统下用c++进行过多线程编程时,不同操作系统API提供了相同或是相似的功能,但是它们的API的差别却极为悬殊。ACE_Thread提供了对不同OS的线程调用的简单包装,通过一个通用的接口进行处理线程创建、挂起、取消和删除等问题,为跨平台开发提供了支持。

1.1、线程入口函数

所有线程必须从一个指定的函数开始执行,该函数称为线程函数,它必须具有下列原型:

void* worker(void *arg) {}

该函数输入一个void *型的参数,可以在创建线程时传入。

1.2、线程基本操作

1.2.1、创建一个线程

一个进程的主线程是由操作系统自动生成,如果你要让一个主线程创建额外的线程,可以通过ACE_Thread::spawn()实现,该函数一般的使用方式如下:

 

   ACE_thread_t threadId;

   ACE_hthread_t threadHandle;

 

   ACE_Thread::spawn(

       (ACE_THR_FUNC)worker,         //线程执行函数

       NULL,                        //执行函数参数

       THR_JOINABLE | THR_NEW_LWP,

       &threadId,

       &threadHandle

       );

 

为了简化,也可以使用其默认参数,直接使用ACE_Thread::spawn((ACE_THR_FUNC)worker) 来创建一个worker的线程.另外,ACE还提供了ACE_Thread::spawn_n函数来创建多个线程。

 

1.2.2、终止线程

在线程函数体中ACE_Thread::exit()调用即可终止线程执行。

1.2.3、设定线程的相对优先级

当一个线程被首次创建时,它的优先级等同于它所属进程的优先级。一个线程的优先级是相对于其所属的进程的优先级而言的。可以通过调用ACE_Thread::setprio函数改变线程的相对优先级,该函数的调用方式如下:

ACE_Thread::setprio(threadHandle,ACE_DEFAULT_THREAD_PRIORITY)

1.2.4、挂起及恢复线程

挂起线程可以通过来实现,它能暂停一个线程的执行,其调用方式如下:

ACE_Thread::suspend(threadHandle) 。

相应的,可以通过ACE_Thread::resume(threadHandle)恢复被挂起的线程的执行。

1.2.5、等待线程结束

在主函数中调用ACE_Thread::join(threadHandle)可阻塞主函数,直道线程结束才能继续执行。

1.2.6、停止线程

在主函数中调用ACE_Thread::cancel(threadHandle)可停止线程的执行(在Unix底下可以,而在windows下好像不起作用,有待检验)。

1.2.4、线程操作示例

#include "ace/Thread.h"

#include "ace/Synch.h"

 

#include <iostream>

using namespace std;

 

void* worker(void *arg)

{

    for(int i=0;i<10;i++)

    {

        ACE_OS::sleep(1);

        cout<<endl<<"hello world"<<endl;

    }

    return NULL;

}

 

int main(int argc, char *argv[])

{

    ACE_thread_t threadId;

    ACE_hthread_t threadHandle;

 

    ACE_Thread::spawn(

        (ACE_THR_FUNC)worker,        //线程执行函数

        NULL,                        //执行函数参数

        THR_JOINABLE | THR_NEW_LWP,

        &threadId,

        &threadHandle

        );

   

    ACE_Thread::join(threadHandle);

 

    return 0;

}

1.2、面向对象的线程封装

在上一节中,线程的操作使用ACE_Thread包装,但是线程操作还是面向过程的函数操作,而ACE_Task_Base对常用线程处理进行了面向对象包装,通过ACE_Task_Base,能对线程进行更好的操作。

要创建任务,需要进行以下步骤:

1.2.1、实现服务初始化和终止方法

open()方法应该包含所有专属于任务的初始化代码。其中可能包括诸如连接控制块、锁和内存这样的资源。

close()方法是相应的终止方法。

1.2.2、调用启用(activation)方法

在主动对象实例化后,你必须通过调用activate()启用它。要在主动对象中创建的线程的数目,以及其他一些参数,被传递给activate()方法。activate()方法会使svc()方法成为所有它生成的线程的启动点。该方法的声明如下:

 virtual int activate (long flags = THR_NEW_LWP | THR_JOINABLE |THR_INHERIT_SCHED,

                        int n_threads = 1,

                       int force_active =0,

                        long priority =ACE_DEFAULT_THREAD_PRIORITY,

                        int grp_id = -1,

                        ACE_Task_Base *task =0,

                        ACE_hthread_tthread_handles[] = 0,

                        void *stack[] = 0,

                        size_t stack_size[] =0,

                        ACE_thread_tthread_ids[] = 0,

                        const char* thr_name[]= 0);

第一个参数线程创建标志flags可为如下组合:     

THR_CANCEL_DISABLE : 不允许这个线程被取消;    

THR_CANCEL_ENABLE  : 允许这个线程被取消;    

THR_CANCEL_DEFERRED: 只允许延迟的取消;     

THR_BOUND   : 创建一个线程,并绑定到一个可由内核调度的实体上;     

THR_NEW_LWP : 创建一个内核级线程;该标志影响进程的并发属性;对"未绑定线程"来说,其预想的并发级别是增1,也就是添加一个新的内核线程到可用的线程池中,以运行用 户线程;在不支持N:M混合线程模型的OS平台上,该标志会被忽略;     

THR_DETACHED : 创建一个分离的线程;这就意味着这个线程的退出状态不能被其它线程访问;当这个线程退出的时候,其线程ID和它所占用的资源会被OS自动回收;     

THR_JOINABLE  : 允许新创建的线程被"会合(join)";这就意味着这个线程的退出状态能够被其它线程访问,可以通过join()方法来访问它的退出状态,但是它的线程 ID和它所占用的资源不会被OS回收;所有ACE线程创建方法的默认行为都是THR_JOINABLE;     

THR_SUSPENDED  : 创建一个线程,但让其处在挂起状态;    

THR_DAEMON  : 创建一个看守(daemon)线程;     

THR_SCHED_FIFO : 如果可用,使用FIFO政策调度新创建的线程;     

THR_SCHED_RR   : 如果可用,使用round-robin方案调度新创建的线程;    

THR_SCHED_DEFAULT : 使用操作系统上可用的无论哪种默认调度方案;     

THR_SCOPE_SYSTEM : 新线程在系统调度争用空间中创建,永久绑定于新创建的内核线程;

THR_SCOPE_PROCESS : 新线程在进程调度争用空间中创建,也就是说,它将作为一个用户线程运行;      

线程的这些属性标志通过逻辑或运算符"|"串在一起,并把它作为ACE_Task_Base::active()方法的第一个参数传递给该方法。

1.2.3、实现任务专有的处理方法

如上面所提到的,在主动对象被启用后,各个新线程在svc()方法中启动。应用开发者必须在子类中定义和实现svc()方法。

1.2.4、一个简单的示例

// main.cpp : 定义控制台应用程序的入口点。

#include "ace/Task.h"

#pragma comment(lib,"ACEd.lib")

 

class MyThreadTask : public ACE_Task_Base

{

public:

         virtual int open (void *args = 0)

         {

                   return activate();

         }

 

         virtual int svc()

         {

                   int i=0;

                   while(i++<1000)

                   {

                            ACE_DEBUG((LM_DEBUG, "thread is running\n"));

                   }

 

                   return 1;

         }

 

         virtual int close(void)

         {

                   ACE_DEBUG((LM_DEBUG, "thread closes down \n"));

                   return 0;

         }

};

 

int main(int argc, char* argv[])

{

         MyThreadTask mytask;

         mytask.open();  //启动线程

 

         mytask.wait();   //等待线程停止

 

         ACE_DEBUG((LM_DEBUG, "thread over! \n"));

 

         return 0;

}

 

二、线程间通信

ACE_Task继承自ACE_Task_Base,为进程间的通信提供了一种基于消息的编程模式。ACE 中的每个任务(ACE_Task)都有一个底层消息队列(ACE_Message_Queue)。这个消息队列被用作任务间通信的一种方法。当一个任务想要与另一任务“谈话”时,它创建一个消息,并将此消息放入putq()到它想要与之谈话的任务的消息队列。接收任务通常用 getq()从消息队列里获取消息。

ACE_Task的消息队列可以由多个处理线程共享使用,所以需要提供同步模式,例如ACE_MT_SYNCH和ACE_NULL_SYNCH分别表示基于多线程的同步和不使用同步,这个参数是ACE_Task的一个模板参数。

例如:

1)消息的处理

class My_Task : public ACE_Task<ACE_MT_SYNCH>

{

public:

         virtual int svc()

         {

                   ACE_Message_Block* msg;

                   while(-1!=getq(msg))

                   {

                            // 在这里处理接收到的消息msg

                            //...............................

 

                            //回复消息

                            reply(msg);

                   }

         }

};

ACE_Task::getq在当前消息队列没有消息时都会阻塞到有消息可用为止。

2)线程的开启

假设My_Task是一个基于ACE_Task的类,创建一个唯一的My_Task实例,这个可以通过

typedef ACE_Singleton<MyTask, SYNCH_METHOD> MYTASK;

在适当位置(一般是程序开始的时候),让My_Task开始工作

MYTASK::intance()->activate(THR_NEW_LWP | THR_JOINABLE|THR_INHERIT_SCHED , 5,);//开启5个线程

3)消息的发送

在有消息发生的时候发送消息

ACE_Message_Block * msg;

//fill the msg

MYTASK::intance()->putq(msg);

ACE_Task::putq只是完成将消息插入到消息队列的工作,理论上它应该立刻返回,但实际上,ACE_Task的消息队列有容量大小限制,这个限制由我们自己限定,当当前消息队列满时,ACE_Task::putq将阻塞一直到可以插入为止。

 

消息的类型与消息中所含有的数据请参见ACE_Message_Block类的声明。

0 0
原创粉丝点击