读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)而直接访问在内存存储位置的数据结构.也就是说,它通过把键值通过一个函数的计算,映射到表中一个位置来访问记录,这加快了查找速度.这个映射函数称做散列函数,存放记录 ...
随机推荐
- 1648: [Usaco2006 Dec]Cow Picnic 奶牛野餐
1648: [Usaco2006 Dec]Cow Picnic 奶牛野餐 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 432 Solved: 270[ ...
- 挂接命令(mount) 【转】
收藏些文章,以后可能有用,文章出自:http://tutu.spaces.eepw.com.cn/articles/article/item/70737 挂接命令(mount) 首先,介绍一下挂接(m ...
- Java引领新生活
阅读邹欣老师的博客,谈谈你期望的师生关系是什么样的? 我觉得师生关系应当是亲密无间,课上老师讲解学生配合,课下师生交流启发思考. 你有什么技能(学习,棋类,球类,乐器,艺术,游戏,......)比大多 ...
- (29)网络编程之TCP通信协议
TCP通信协议特点: 1.tcp协议是基于IO流进行数据的传输,是面向链接的. 2.tcp进行数据传输的时候,数据没有大小限制的. 3.面向链接,通过三次握手的机制,保证数据的完整性,是一个可靠的协议 ...
- 更改服务器的SID 加入域控制器提示SID重复
启动Windows2008.2012进入系统后,打开“CMD窗口”并进入到"C:\windows\system32\sysprep"目录后再输入“sysprep /generali ...
- 十分钟搭建redis单机版 & java接口调用
本次单机版redis服务器搭建采用的包为redis-3.0.0.tar.gz,主要是记录下安装的心得,不喜勿喷! 一.搭建redis服务器单机版 1.上传redis-3.0.0.tar.gz到服务器上 ...
- iOS开发之Xib和storyboard对比
相同点: (2)都用来描述软件界面 (2)都用Interface Builder工具来编辑 不同点: (1)Xib是轻量级的,用来描述局部的UI界面 (2)Storyboard是重量级的,用来描述整个 ...
- nfs mount:reason given by server: Permission denied
遇到nfs mount的问题,以前从未遇到过,问题出的很奇怪,现象是 mount: 10.1.10.22:/cicro failed, reason given by server: Permissi ...
- 老李推荐:第6章1节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览 2
事件要到那里去? 每个事件源处理类都维护着一个自己的事件队列, 在Monkey中叫做CommandQueue,里面装的是每个具体的MonkeyEvent事件.当来自网络的字串命令被翻译成对应的Monk ...
- 简单分析下用yii2的yii\helpers\Html类和yii.js实现的post请求
yii2提供了很多帮助类,比如Html.Url.Json等,可以很方便的实现一些功能,下面简单说下这个Html.用yii2写view时时经常会用到它,今天在改写一个页面时又用到了它.它比较好用的地方就 ...