ACL緩存開發

  在編寫高效的程序時,內存緩存有時是很是有用的,提到緩存,你們可能會很容易想到可使用哈希表這種最經常使用的方式來緩存內存對象,但哈希的實現代碼通常不具有兩項功能:緩存過時時間、緩存數量限制,若是要增長對此二項功能的支持,通常須要增長輔助的鏈表結構。若是使用ACL裏的 ACL_CACHE,則在高效緩存的前提下支持這兩項功能。緩存

  下面是ACL_CACHE的數據結構及經常使用的接口調用:數據結構

1、數據結構及經常使用接口說明多線程

 

一、數據結構定義函數

/**
 * 緩衝池對象結構定義
 */
typedef struct ACL_CACHE { 
	ACL_HTABLE *table;	/**< 哈希表 */
	ACL_RING ring;		/**< 將被刪除的對象的數據鏈表 */
	int   max_size;                 /**< 緩存池容量大小限制值 */
	int   size;		                /**< 當前緩存池中的緩存對象個數 */
	int   timeout;		        /**< 每一個緩存對象的生存時長(秒) */

	/**< 釋放用戶動態對象的釋放回調函數 */
	void  (*free_fn)(const ACL_CACHE_INFO*, void *);
	acl_pthread_mutex_t lock;	/**< 緩存池鎖 */
	ACL_SLICE *slice;		/**< 內存切片對象 */

	/* for acl_iterator */

	/* 取迭代器頭函數 */
	const void *(*iter_head)(ACL_ITER*, struct ACL_CACHE*);
	/* 取迭代器下一個函數 */
	const void *(*iter_next)(ACL_ITER*, struct ACL_CACHE*);
	/* 取迭代器尾函數 */
	const void *(*iter_tail)(ACL_ITER*, struct ACL_CACHE*);
	/* 取迭代器上一個函數 */
	const void *(*iter_prev)(ACL_ITER*, struct ACL_CACHE*);
	/* 取迭代器關聯的當前容器成員結構對象 */
	const ACL_CACHE_INFO *(*iter_info)(ACL_ITER*, struct ACL_CACHE*);
} ACL_CACHE;

/**
 * 緩存池中存儲的緩存對象
 */
typedef struct ACL_CACHE_INFO {
	char *key;		/**< 健值 */
	void *value;		/**< 用戶動態對象 */
	int   nrefer;		/**< 引用計數 */
	time_t when_timeout;	/**< 過時時間截 */
	ACL_RING entry;		/**< 內部數據鏈成員 */
} ACL_CACHE_INFO;

 

二、建立與釋放接口spa

/**
 * 建立一個緩存池,並設置每一個緩存對象的最大緩存時長及該緩存池的空間容量限制
 * @param max_size {int} 該緩存池的容量限制
 * @param timeout {int} 每一個緩存對象的緩存時長
 * @param free_fn {void (*)(void*)} 用戶級的釋放緩存對象的函數
 * @return {ACL_CACHE*} 緩存池對象句柄
 */
ACL_API ACL_CACHE *acl_cache_create(int max_size, int timeout,
	void (*free_fn)(const ACL_CACHE_INFO*, void*));

/**
 * 釋放一個緩存池,並自動調用 acl_cache_create()/3 中的釋放函數釋放緩存對象
 * @param cache {ACL_CACHE*} 緩存池對象句柄
 */
ACL_API void acl_cache_free(ACL_CACHE *cache);

 

三、增、刪、查接口.net

/**
 * 向緩存池中添加被緩存的對象
 * @param cache {ACL_CACHE*} 緩存池對象句柄
 * @param key {const char*} 緩存對象的健值
 * @param value {void*} 動態緩存對象
 * @return {ACL_CACHE_INFO*} 緩存對象所依附的結構對象,其中的 value 與用戶的對象相同,
 *   若是返回 NULL 則表示添加失敗,失敗緣由爲:緩存池太大溢出或相同健值的對象存在
 *   且引用計數非0; 若是返回非 NULL 則表示添加成功,若是對同一健值的重複添加,會用
 *   新的數據替換舊的數據,且舊數據調用釋放函數進行釋放
 */
ACL_API ACL_CACHE_INFO *acl_cache_enter(ACL_CACHE *cache, const char *key, void *value);

/**
 * 從緩存池中查找某個被緩存的對象
 * @param cache {ACL_CACHE*} 緩存池對象句柄
 * @param key {const char*} 查詢健
 * @return {void*} 被緩存的用戶對象的地址,爲NULL時表示未找到
 */
ACL_API void *acl_cache_find(ACL_CACHE *cache, const char *key);

/**
 * 從緩存池中查找某個被緩存的對象所依附的緩存信息對象
 * @param cache {ACL_CACHE*} 緩存池對象句柄
 * @param key {const char*} 查詢健
 * @return {ACL_CACHE_INFO*} 緩存信息對象地址,爲NULL時表示未找到
 */

/**
 * 從緩存池中刪除某個緩存對象
 * @param cache {ACL_CACHE*} 緩存池對象句柄
 * @param key {const char*} 健值
 * @return {int} 0: 表示刪除成功; -1: 表示該對象的引用計數非0或該對象不存在
 */
ACL_API int acl_cache_delete2(ACL_CACHE *cache, const char *key);

 

四、同步互斥接口線程

/**
 * 加鎖緩存池對象,在多線程時用
 * @param cache {ACL_CACHE*} 緩存池對象句柄
 */
ACL_API void acl_cache_lock(ACL_CACHE *cache);

/**
 * 解鎖緩存池對象,在多線程時用
 * @param cache {ACL_CACHE*} 緩存池對象句柄
 */
ACL_API void acl_cache_unlock(ACL_CACHE *cache);

 

五、遍歷接口設計

/**
 * 遍歷緩存中的全部對象
 * @param cache {ACL_CACHE*} 緩存池對象句柄
 * @param walk_fn {void (*)(ACL_CACHE_INFO*, void*)} 遍歷回調函數
 * @param arg {void *} walk_fn()/2 中的第二個參數
 */
ACL_API void acl_cache_walk(ACL_CACHE *cache, void (*walk_fn)(ACL_CACHE_INFO *, void *), void *arg);

  固然,由於 ACL_CACHE 符合 ACL_ITER 通用迭代器(C語言中迭代器的設計與使用 )的規則要求,因此也能夠採用ACL通用迭代方式遍歷ACL_CACHE內部緩存對象,以下:code

typedef struct {
  char  name[32];
  char  dummy[32];
} MY_DAT;

static free_mydat_fn(const ACL_CACHE_INFO *info, void *arg)
{
  MY_DAT *mydat = (MY_DAT*) mydat;

  acl_myfree(mydat);
}

void test(void)
{
  ACL_CACHE *cache;
  ACL_ITER iter;
  MY_DAT *mydat;
  char  key[32];

  /* 建立緩存對象句柄 */
  cache = acl_cache_create(100, 60, free_mydat_fn);

  /* 向緩存中添加緩存數據 */
  for (i = 0; i < 10; i++) {
    mydat = (MY_DAT*) acl_mymalloc(sizeof(MY_DAT));
    snprintf(key, sizeof(key), "key:%d", i);
    snprintf(mydat->name, sizeof(mydat->name), "name: %d", i);
    (void) acl_cache_enter(cache, key, mydat);
  }

  /* 遍歷全部緩存數據 */
  acl_foreach(iter, cache) {
    const MY_DAT *mydat = (const MY_DAT*) iter.data;
    printf(">>>name: %s\n", mydat->name);
  }

  /* 釋放緩存句柄並清除緩存數據 */
   acl_cache_free(cache);
}

 

 除了以上幾個經常使用接口外,ACL_CACHE 模塊還提供了其它方便使用的接口調用方式,參見: lib_acl/include/stdlib/acl_cache.h 頭文件說明。orm

 

2、舉例

 下面是一個完整使用 ACL_CACHE 的例子:

#include "lib_acl.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

/* 用戶自定義數據結構 */
typedef struct {
	char *buf;
	int   len;
} MYOBJ;

/**
 * 釋放用戶數據回調數據
 * @param info {const ACL_CACHE_INFO*} 用戶緩存數據所依附的ACL_CACHE 的某個數據對象
 * @param arg {void*} 用戶數據對象以 void * 表示
 */
static void free_fn(const ACL_CACHE_INFO *info, void *arg)
{
	MYOBJ *o = (MYOBJ*) arg;

	printf("%s: when_timeout: %ld, now: %ld, len: %d, deleted\n",
		info->key, (long) info->when_timeout, (long) time(NULL), o->len);

	acl_myfree(o->buf);
	acl_myfree(o);
}

/**
 * 建立一個新的用戶數據對象
 * @param len {int} MYOBJ.buf 的長度
 * @return {MYOBJ*}
static MYOBJ *myobj_new(int len)
{
	MYOBJ *o = (MYOBJ*) acl_mymalloc(sizeof(MYOBJ));

	o->buf = (char*) acl_mymalloc(len <= 0 ? 100 : len);
	o->len = len;
	return (o);
}

/**
 * 遍歷數據數據緩存中每個數據對象的回調函數
 * @param info {ACL_CACHE_INFO*}
 * @arg {void*} 用戶數據對象
 */
static void walk_fn(ACL_CACHE_INFO *info, void *arg)
{
	MYOBJ *o = (MYOBJ*) info->value;

       assert(info->value == arg);
	printf("%s: size: %d; when_timeout: %ld\n", info->key, o->len, (long) info->when_timeout);
}

static void usage(const char *procname)
{
	printf("usage: %s -h [help] -n max_size -t timeout\n", procname);
}

int main(int argc, char *argv[])
{
	int   i, n = 100, ch, timeout = 1;
	ACL_CACHE_INFO *info;
	ACL_CACHE *cache;
	char  key[32];
	MYOBJ *o;

	while ((ch = getopt(argc, argv, "hn:t:")) > 0) {
		switch (ch) {
			case 'h':
				usage(argv[0]);
				exit (0);
			case 'n':
				n = atoi(optarg);
				break;
			case 't':
				timeout = atoi(optarg);
				break;
			default:
				break;
		}
	}
	
        /* 建立緩存句柄 */
	cache = acl_cache_create(n, timeout, free_fn);

        /* 向緩存中添加用戶緩存數據 */
	for (i = 0; i < n + 5; i++) {
		o = myobj_new(i + 1);
		snprintf(key, sizeof(key), "key(%d)", i);
		assert(acl_cache_enter(cache, key, o));
		printf("add one: %s\n", key);
		sleep(1);
	}

	printf("\nfirst walk cache, cache size: %d\n", acl_cache_size(cache));

	/* 遍歷全部緩存數據 */
	acl_cache_walk(cache, walk_fn, NULL);
	printf("\nfirst call acl_cache_timeout, size: %d\n", acl_cache_size(cache));

	/* 過時的緩存數據被自動刪除 */
	acl_cache_timeout(cache);
	printf(">>>after first acl_cache_timeout, second walk cache, cache's size: %d\n", acl_cache_size(cache));

	/* 遍歷全部緩存數據 */
	acl_cache_walk(cache, walk_fn, NULL);

	printf("\n");
	i = 0;

	/* 休眠以使有些緩存數據過時 */
	while (i++ < 5) {
		printf("slee one second, i=%d\n", i);
		sleep(1);
	}

	printf("\nsecond call acl_cache_timeout, size: %d\n", acl_cache_size(cache));

	/* 過時的緩存數據被自動刪除 */
	acl_cache_timeout(cache);
	printf(">>>after second acl_cache_timeout, third walk_cache, cache's size: %d\n", acl_cache_size(cache));

	/* 遍歷全部緩存數據 */
	acl_cache_walk(cache, walk_fn, NULL);

	/* 查詢緩存對象 */
	o = (MYOBJ*) acl_cache_find(cache, "key(5)");
	if (o == NULL)
		printf("\n>>>key(5) not exist\n");
	else
		printf("\n>>>key(5): len: %d\n", o->len);

	/* 定位用戶緩存數據所依附的緩存對象 */
	info = acl_cache_locate(cache, "key(11)");
	if (info == NULL)
		printf("\n>>>key(11) not exist\n");
	else {
		o = (MYOBJ*) info->value;
		printf("\n>>>key(11): len: %d, when_timeout: %ld\n", o->len, (long) info->when_timeout);
	}

	printf("\nfree cache, size: %d\n", acl_cache_size(cache));

	/* 釋放緩存並清除全部用戶緩存數據 */
	acl_cache_free(cache);
	return (0);
}

ACL 庫下載位置:http://acl.sourceforge.net/
我的微博:http://weibo.com/zsxxsz
相關文章
相關標籤/搜索