读APUE分析散列表的使用
最近学习APUE读到避免线程死锁的部分,看到部分源码涉及到避免死锁部分,源码使用了散列表来实现对结构(struct)的存储与查找。
本文不讨论代码中的互斥量部分。
#include <stdlib.h>
#include <pthread.h> #define NHASH 29
#define HASH(id) (((unsigned long)id)%NHASH) struct foo *fh[NHASH]; pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER; struct foo {
int f_count;
pthread_mutex_t f_lock;
int f_id;
struct foo *f_next; /* protected by hashlock */
/* ... more stuff here ... */
}; struct foo *
foo_alloc(int id) /* allocate the object */
{
struct foo *fp;
int idx; if ((fp = malloc(sizeof(struct foo))) != NULL) {
fp->f_count = ;
fp->f_id = id;
if (pthread_mutex_init(&fp->f_lock, NULL) != ) {
free(fp);
return(NULL);
}
idx = HASH(id);
pthread_mutex_lock(&hashlock);
fp->f_next = fh[idx];
fh[idx] = fp;
pthread_mutex_lock(&fp->f_lock);
pthread_mutex_unlock(&hashlock);
/* ... continue initialization ... */
pthread_mutex_unlock(&fp->f_lock);
}
return(fp);
} void
foo_hold(struct foo *fp) /* add a reference to the object */
{
pthread_mutex_lock(&fp->f_lock);
fp->f_count++;
pthread_mutex_unlock(&fp->f_lock);
} struct foo *
foo_find(int id) /* find an existing object */
{
struct foo *fp; pthread_mutex_lock(&hashlock);
for (fp = fh[HASH(id)]; fp != NULL; fp = fp->f_next) {
if (fp->f_id == id) {
foo_hold(fp);
break;
}
}
pthread_mutex_unlock(&hashlock);
return(fp);
} void
foo_rele(struct foo *fp) /* release a reference to the object */
{
struct foo *tfp;
int idx; pthread_mutex_lock(&fp->f_lock);
if (fp->f_count == ) { /* last reference */
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_lock(&hashlock);
pthread_mutex_lock(&fp->f_lock);
/* need to recheck the condition */
if (fp->f_count != ) {
fp->f_count--;
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_unlock(&hashlock);
return;
}
/* remove from list */
idx = HASH(fp->f_id);
tfp = fh[idx];
if (tfp == fp) {
fh[idx] = fp->f_next;
} else {
while (tfp->f_next != fp)
tfp = tfp->f_next;
tfp->f_next = fp->f_next;
}
pthread_mutex_unlock(&hashlock);
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_destroy(&fp->f_lock);
free(fp);
} else {
fp->f_count--;
pthread_mutex_unlock(&fp->f_lock);
}
}
代码来自:http://blog.csdn.net/abcef31415926/article/details/53898325
取余法散列表:书中使用的是取余法来构建散列表,通过使用第5行定义的宏函数来计算(唯一计算)出每个ID(struct内部属性,保证struct唯一性)对应的散列表中的直接索引值。
而对于一个本例中已经构建出来的散列表,它的本质是这样的:

上图的0-15的实现实质其实也是指针(有自己指向的对象的Value值),这也是我之前没有弄明白的地方。
之前一直以为散列表是一个链表(就是里面存了,n个指针,每个指针相互首尾串联)。
然而不然,散列表的每个元素都是一个链表的头指针(即假设0-15都不为空,则一个散列表有16个相互独立的链表),添加新结构进入散列表的方法则是将结构本身代替索引位置的头指针,并指向他。
代码分析(背景灰色部分):
4-5 :使用求余的方法构建散列函数,使得每一个ID都能通过散列函数计算出的值落中在散列表定义数组的索引区间内。
7:定义一个符合上述条件区间长度的数组,数组内的每个元素都是struct foo *类型,初始值为NULL。
foo_alloc(int id)部分:该函数的作用是在散列表中添加一个未初始化的id为id的新结构。
32:计算散列表中的索引(计算应该将新结构放在第几个链表里)。
34:将该结构的next节点指向目前该位置上链表的头结点。
35:将链表的头结点设置成自己的指针。
foo_find(int id)部分:该函数的作用是通过结构的id,在散列表中找到结构的指针。
58:通过散列函数计算出数组的索引值,并确保在该索引位置上的链表中从头到尾使用循环查找。
59-63:如果链表中的某个结点满足id值和传入的id值相等的条件,就确信已经找到了该结构,返回这个指针。
foo_rele(struct foo *fp)部分:释放这个引用(指针),如果这是最后一个引用释放这个对象的内存空间。
75:如果这是当前的引用计数是1(该指针是最后一个指向对象的指针)。
87-88:通过散列函数计算索引,并获取链表第一个结点的地址。
89-91:如果当前结点是首结点,将链表的首指针(该位置的指针)设置为该元素的next指针。
92-95:向后寻找结点,直到该节点(结点a)的next是目标元素(结点b)的指针,把结点a的next设置为结点b的next。
99:该结点已经不在散列表内,释放对象的内存。
读APUE分析散列表的使用的更多相关文章
- Java HashMap源码分析(含散列表、红黑树、扰动函数等重点问题分析)
写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...
- 哈希表(散列表),Hash表漫谈
1.序 该篇分别讲了散列表的引出.散列函数的设计.处理冲突的方法.并给出一段简单的示例代码. 2.散列表的引出 给定一个关键字集合U={0,1......m-1},总共有不大于m个元素.如果m不是很大 ...
- 哈希表(散列表)—Hash表解决地址冲突 C语言实现
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.具体的介绍网上有很详 ...
- 散列表(hash table)——算法导论(13)
1. 引言 许多应用都需要动态集合结构,它至少需要支持Insert,search和delete字典操作.散列表(hash table)是实现字典操作的一种有效的数据结构. 2. 直接寻址表 在介绍散列 ...
- HashTable(散列表)
最近都在研究数据结构,关于hashtable,或者叫做散列表,过去一直不了解是什么东西,现在终于明白了. 所谓hashtable,就是某组key,通过某个关系(函数),得到一个与之对应的映射值(在计算 ...
- &11,散列表
#1,是什么? 散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个 ...
- [转载] 散列表(Hash Table)从理论到实用(上)
转载自:白话算法(6) 散列表(Hash Table)从理论到实用(上) 处理实际问题的一般数学方法是,首先提炼出问题的本质元素,然后把它看作一个比现实无限宽广的可能性系统,这个系统中的实质关系可以通 ...
- Java学习笔记(二十)——Java 散列表_算法内容
[前面的话] 周末,本来打算找人去玩,结果没找到,所以我只好有学习了. 为什么会学习散列表,因为要使用HashMap?因为在做项目的时候,在服务器和客户端需要传输DTO,而传输的属性是动态增加的,所以 ...
- 散列表 (Hash table,也叫哈希表)
散列表是根据关键字(Key value)而直接访问在内存存储位置的数据结构.也就是说,它通过把键值通过一个函数的计算,映射到表中一个位置来访问记录,这加快了查找速度.这个映射函数称做散列函数,存放记录 ...
随机推荐
- Servlet 3.0/3.1 中的异步处理
在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式处理请求,即每一次Http请求都由某一个线程从头到尾负责处理.如果一个请求需要进行IO操作,比如访问数据库.调 ...
- 485. Max Consecutive Ones
题目 Given a binary array, find the maximum number of consecutive 1s in this array. Example 1: Input: ...
- HDFS Namenode启动过程
文章作者:luxianghao 文章来源:http://www.cnblogs.com/luxianghao/p/6564032.html 转载请注明,谢谢合作. 免责声明:文章内容仅代表个人观点, ...
- MySQL入门(上)
1 课程回顾 自定义标签&编码实战 1)自定义标签步骤: 1.1 编写标签处理器类,继承SimpleTagSupport类,覆盖doTag方法 1.2 在WEB-INF目录下建立tld文件,在 ...
- 【树莓派】Linux 测网速及树莓派源
这篇文章比较杂,其中包含三点:linux环境中测试网络速度,树莓派下载软件的源,部分我写好的脚本: 一.Linux 测网速 Linux 测网速: sar -n DEV 1 100 1代表一秒统计并显示 ...
- Spring-java代理技术总结
Spring 中采用JDk的动态代理和CGLib代理技术在运行期间织入增强,所以用户不需要装备特殊的编译器或者类装载器就可以使用AOP功能. 要使用jdk的动态代理,目标类必须实现接口,而CGLib代 ...
- MyBatis框架原理(三)
一.框架结构原理图 二.MyBatis框架执行流程 三.总结 原理详解: MyBatis应用程序根据XML配置文件创建SqlSessionFactory,SqlSessionFactory在根据配置, ...
- Android 中基于 Binder的进程间通信
摘要:对 Binder 工作机制进行了分析. 首先简述 Android 中 Binder 机制与传统的 Linux 进程间的通信比较,接着对基于 Binder 进程间通信的过程分析 最后结合开发实例 ...
- Alamofire源码解读系列(十)之序列化(ResponseSerialization)
本篇主要讲解Alamofire中如何把服务器返回的数据序列化 前言 和前边的文章不同, 在这一篇中,我想从程序的设计层次上解读ResponseSerialization这个文件.更直观的去探讨该功能是 ...
- ArcEngine保存栅格数据至rastercatalog
将栅格数据(IRasterDataset)直接保存到数据库中很常见,但是保存到栅格目录下就比较少见,好不容易才找到,在这里记录一下. public void saveRasterDs2Catalog( ...