rn_xtcxyczjh-7 并发[线程3 嵌套锁与装饰模式]

来源:互联网 发布:淘宝客计划管理设置 编辑:程序博客网 时间:2024/06/14 03:04

2015.10.30
读xtcxyczjh(系统程序员成长计划)—- 学习程序设计方法。
个人笔记对应代码保存地址:y15m10d30
作者实现的是递归锁。

1. 需求简述

实现一个嵌套锁,要求如下:
- 嵌套锁仍然兼容Locker接口。
- 嵌套锁的实现不依赖于特定平台。

2. 准备

读《xtcxyczjh》P.46-P.48:
- 嵌套加锁造成的死锁:在同一线程中,连续加锁(程序会被阻塞在第二次线程加锁处)。
- 装饰模式:不改变对象的本质(接口)的前提下,给对象(锁)添加附加的功能(对一把锁进行装饰,不改变它的接口,但给它加上嵌套的功能)。

3. 代码

(1) 分析

  • 嵌套锁的实现算法(P.46 —- 可能会导致线程中无必要加锁的代码加锁,如父函数加锁后调用前面有一大段代码都不用加锁的子函数)。
  • 与平台相关的功能模块要用回调函数来抽象,如获取当前线程ID的函数、创建线程嵌入锁的函数等。
  • 描述嵌入锁(装饰锁)的数据结构(保存线程的ID、计数、线程锁接口、获取线程ID函数指针等元素)。

(2) 实现

[1]装饰锁接口
因为不能改变线程锁的接口,故而需要另定义一个数据结构类型来描述“嵌入锁”以及“获取线程ID回调函数“等功能。利用C语言结构体实现变长数组的机制,将新定义的数据结构加到线程锁接口的对象中,这样就能够实现装饰模式

描述装饰锁的数据结构。

/* locker.h *//* 定义不依赖于平台的线程锁接口 */#ifndef LOCKER_H#define LOCKER_H//……//锁接口struct _Locker {    LockerLockFuncT     lock;    LockerUnlockFuncT   unlock;    char                dcrt[0];};//装饰锁接口typedef int (*pGetThreadSelfIDFuncT)(void);typedef struct _DcrtLockT {    int                     owner;      //拥有锁的线程ID    int                     count;      //加锁计数器    pGetThreadSelfIDFuncT   tid_self;   //指向获取线程ID函数的函数指针}DcrtLockT;#endif

[2] 嵌入锁函数
创建嵌入锁函数。在cpthread.c中,将create_thread_locker函数修改为以下内容:

/* cpthread.c *//* 调用POSIX thread库中的函数定义线程线程相关函数 *///……//用户自定实现线程锁初始化函数Locker *create_thread_nest_locker(void){    //在这里添加您初始化线程嵌入锁的代码    Locker  *locker = NULL;    locker  = (Locker *)malloc(sizeof(Locker) + sizeof(DcrtLockT));    if (NULL != locker) {        DcrtLockT   *pdcrt = NULL;        pdcrt   = (DcrtLockT *)locker->dcrt;        locker->lock    = lock_nest_thread;        locker->unlock  = unlock_nest_thread;        pdcrt->owner    = 0;        pdcrt->count    = 0;        pdcrt->tid_self = get_thread_id;    }    //初始化此平台下的线程锁    lock_nest_init();    return locker;}

locker = (Locker *)malloc(sizeof(Locker) + sizeof(DcrtLockT));语句用DcrtLockT装饰了锁对象locker。装饰模式最有用的地方在于,它给单个对象增加功能,但不是影响调用者,即使加了多级装饰,调用者也不用关心。

根据创建线程锁函数的更改,修改其它地方的代码:

/* cpthread.h *//* 定义线程相关,或声明定义在cpthread.c中的调用POSIX thread库的函数 */#ifndef CPTHREAD_H#define CPTHREAD_H//……Locker *create_thread_nest_locker(void);//……#endif/* cpthread.c *//* 调用POSIX thread库中的函数定义线程线程相关函数 *///……//用户自定义实现获取线程ID函数static int get_thread_id(void){    //在这里添加获取当前线程ID的代码    return pthread_self();}//……

lock_nest_thread和unlock_nest_thread分别为线程加锁和解锁函数。

加锁函数。

/* cpthread.c *//* 调用POSIX thread库中的函数定义线程线程相关函数 *///……//用户自定义初始化线程锁函数static RetT lock_nest_thread(Locker *locker){    //在这里定义你的线程加锁代码    int ret = 0;    DcrtLockT   *pdcrt_locker   = (DcrtLockT *)locker->dcrt;    if (pdcrt_locker->owner == pdcrt_locker->tid_self()) {        pdcrt_locker->count++;    }else {        ret = sem_init(&mutex, 0, 1);        pdcrt_locker->count = 1;        pdcrt_locker->owner = pdcrt_locker->tid_self();    }    return ret == 0 ? RET_OK : RET_FAIL;}//……

如果当前线程已经加锁,只是增加加锁计数,否则就加锁。

解锁函数。

/* cpthread.c *//* 调用POSIX thread库中的函数定义线程线程相关函数 *///……//用户自定义实现的线程加锁函数static RetT unlock_nest_thread(Locker *locker){    //在这里定义你的线程加锁代码    int ret = 0;    DcrtLockT   *pdcrt_locker   = (DcrtLockT *)locker->dcrt;    return_val_if_p_invalid(pdcrt_locker->owner == pdcrt_locker->tid_self(), RET_FAIL);    pdcrt_locker->count--;    if (pdcrt_locker->count == 0) {        pdcrt_locker->owner = 0;        ret = sem_wait(&mutex);    }    return ret == 0 ? RET_OK : RET_FAIL;}//……

只有当前线程加的锁才能解锁,先减少加锁计数,计数为0时才真正解锁,否则直接返回。

修改原调用create_thread_locker函数的代码:

/* main.c *//* 包含C程序入口 */#include <stdio.h>#include "cpthread.h"#include <assert.h>#include "autotest.h"int main(void){    DlistManageT    *pdl = NULL;    unsigned int    dlen = 10;    single_thread_test(&pdl, dlen, NULL);    free_dlist(pdl);    pdl = NULL;    multi_thread_test(&pdl, dlen, create_thread_nest_locker);    free_dlist(pdl);    return 0;}

因为本次代码都是通过装饰模式来完成的,关于线程锁接口以及双向链表程序都没有被改动过。所以在用户角度下修改代码后,可直接在与Makefile以及程序所在同目录下使用make命令。然后执行./all程序得到以下执行结果:
这里写图片描述
姑且算它运行正常。

读《xtcxyczjh》-Part-VII pnote over.
[2015.10.30-18:15]

0 0
原创粉丝点击