QNX之编写资源管理器(九)

QNX相关历史文章:

Multithreaded Resource Managers

这篇文章主要描述多线程来实现资源管理器。

1. Multithreaded resource manager example

先来看个例子:

#include <errno.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>

/*
 *  define THREAD_POOL_PARAM_T such that we can avoid a compiler
 *  warning when we use the dispatch_*() functions below
 */
#define THREAD_POOL_PARAM_T dispatch_context_t

#include <sys/iofunc.h>
#include <sys/dispatch.h>

static resmgr_connect_funcs_t    connect_funcs;
static resmgr_io_funcs_t         io_funcs;
static iofunc_attr_t             attr;

main(int argc, char **argv)
{
    /* declare variables we'll be using */
    thread_pool_attr_t   pool_attr;
    resmgr_attr_t        resmgr_attr;
    dispatch_t           *dpp;
    thread_pool_t        *tpp;
    dispatch_context_t   *ctp;
    int                  id;

    /* initialize dispatch interface */
    if((dpp = dispatch_create()) == NULL) {
        fprintf(stderr,
                "%s: Unable to allocate dispatch handle.\n",
                argv[0]);
        return EXIT_FAILURE;
    }

    /* initialize resource manager attributes */
    memset(&resmgr_attr, 0, sizeof resmgr_attr);
    resmgr_attr.nparts_max = 1;
    resmgr_attr.msg_max_size = 2048;

    /* initialize functions for handling messages */
    iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &connect_funcs, 
                     _RESMGR_IO_NFUNCS, &io_funcs);

    /* initialize attribute structure used by the device */
    iofunc_attr_init(&attr, S_IFNAM | 0666, 0, 0);

    /* attach our device name */
    id = resmgr_attach(
            dpp,            /* dispatch handle        */
            &resmgr_attr,   /* resource manager attrs */
            "/dev/sample",  /* device name            */
            _FTYPE_ANY,     /* open type              */
            0,              /* flags                  */
            &connect_funcs, /* connect routines       */
            &io_funcs,      /* I/O routines           */
            &attr);         /* handle                 */
    if(id == -1) {
        fprintf(stderr, "%s: Unable to attach name.\n", argv[0]);
        return EXIT_FAILURE;
    }

    /* initialize thread pool attributes */
    memset(&pool_attr, 0, sizeof pool_attr);
    pool_attr.handle = dpp;
    pool_attr.context_alloc = dispatch_context_alloc;
    pool_attr.block_func = dispatch_block;
    pool_attr.unblock_func = dispatch_unblock;
    pool_attr.handler_func = dispatch_handler;
    pool_attr.context_free = dispatch_context_free;
    pool_attr.lo_water = 2;
    pool_attr.hi_water = 4;
    pool_attr.increment = 1;
    pool_attr.maximum = 50;

    /* allocate a thread pool handle */
    if((tpp = thread_pool_create(&pool_attr, 
                                 POOL_FLAG_EXIT_SELF)) == NULL) {
        fprintf(stderr, "%s: Unable to initialize thread pool.\n",
                argv[0]);
        return EXIT_FAILURE;
    }

    /* start the threads, will not return */
    thread_pool_start(tpp);
}

线程池属性pool_attr控制线程池的各个方面,比如新线程启动或终止时调用哪些函数、工作线程的总数、最小数量等等。

2. Thread pool attributes

_thread_pool_attr结构如下:

typedef struct _thread_pool_attr {
  THREAD_POOL_HANDLE_T  *handle;
  THREAD_POOL_PARAM_T   *(*block_func)(THREAD_POOL_PARAM_T *ctp);
  void                  (*unblock_func)(THREAD_POOL_PARAM_T *ctp);
  int                   (*handler_func)(THREAD_POOL_PARAM_T *ctp);
  THREAD_POOL_PARAM_T   *(*context_alloc)(
                            THREAD_POOL_HANDLE_T *handle);
  void                  (*context_free)(THREAD_POOL_PARAM_T *ctp);
  pthread_attr_t        *attr;
  unsigned short        lo_water;
  unsigned short        increment;
  unsigned short        hi_water;
  unsigned short        maximum;
  unsigned              reserved[8];
} thread_pool_attr_t;

填充这个数据结构中的函数,可以是dispatch layer的函数(比如 dispatch_block()...),也可以是resmgr layer的函数(比如 resmgr_block()...),也可以是自己实现的函数。

如果不使用resmgr layer函数,则必须将THREAD_POOL_PARAM_T定义为某种上下文结构,以便库在各种函数间传递。默认情况下,它被定义为resmgr_context_t,但由于这个示例使用的dispatch layer,因此需要定义成dispatch_context_t。需要在include之前定义,因为头文件引用了THREAD_POOL_PARAM_T

上边结构告诉资源管理器如何处理多线程。在开发过程中,在设计资源管理器时应该考虑到多个线程,在测试期间,为了方便调试,可能只有一个线程在运行,在确保资源管理器基本功能稳定后,则需要尝试使用多个线程来运行调试。

  • lo_water,阻塞线程的最小数量
  • increment,每次要创建的数量,以达到lo_water
  • hi_water,阻塞线程的最大数量
  • maximum,任何时候创建线程的最大数量

maximum值应该确保始终有一个处于接收阻塞状态的线程,如果处于最大线程数,那么客户端将阻塞,直到空闲线程准备好接收数据为止。为increment指定的值将减少驱动程序需要创建线程的次数。明智的做法可能是错误的创建更多的线程,而不是一直创建/销毁它们。通过填充lo_water参数,可以随时在MsgReceive()上确定希望接收阻塞的线程数。如果接收阻塞的线程少于lo_water线程,那么increment参数指定一次应该创建多少个线程,这样至少lo_water线程的数量会再次被接收阻塞。一旦线程完成了处理,将返回到block函数。hi_water变量指定接收阻塞线程数量的上限,一旦达到这个限制,线程将自我销毁,以确保接收阻塞的线程数量不会超过hi_water。为了防止线程数量无限制的增加,maximum参数限制了同时运行线程的最大值。
当资源管理器创建线程时,可以通过thread_stack_size来指定堆栈的大小,如果想要指定堆栈的大小,优先级等,可以填充由pthread_attr_t类型指针指向的pool_attr.attr结构。

thread_pool_attr_t结构中,还包含了几个函数指针:

  • block_func(),当需要阻塞等待某些消息时调用;
  • handler_func(),当接收到消息解除阻塞后调用,在这个函数中对消息进行处理;
  • context_alloc(),新线程创建时调用,新线程使用这个上下文来工作;
  • context_free(),当线程退出时,释放这个上下文;
  • unblock_func(),当关闭线程池或改变运行线程的数量时,调用这个函数;

3. Thread pool functions

资源管理器库提供了几个线程池的函数:

  • thread_pool_create(),初始化线程池上下文,返回一个线程池的handle,用于启动线程池;
  • thread_pool_start(),启动线程池,这个函数可能返回,也可能不返回,取决于thread_pool_create()的传入flags
  • thread_pool_destroy(),销毁线程池;
  • thread_pool_control(),控制线程的数量;
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一 昨天约上好友走路,勤快的她,已经在体育中心太极馆开练好几天了。说到要上班了,她莫名苦恼,晚上开始睡不着觉了。 ...
    丁若木阅读 501评论 2 12
  • 顿感,是这样一种神奇的特性,不会因为别人责骂而内心愧疚或者煎熬,不会因为她人赞美,而心欢呼雀跃,带我撑过一些很难熬...
    击空留影阅读 707评论 0 0