在很多第三方提供的接口中,有些会说明此接口是线程安全,正因为这样的说明,会导致很多没有经验的程序员出现莫名其妙的错误,笔者现在以libmemcached的 memcached_pool_release() 和 memcached_pool_fetch()为例进行简单的说明。
在libmemcached的官方文档中,已经说明者两个函数是线程安全的,在使用的过程中可能会出现如下的使用情况:
typedef struct memcached_server_tag
{
memcached_pool_st *memc_svr_pool;
}memcached_server_t;
memcached_server_t *create_memcached_server(const char *memc_server_config,size_t size)
{
assert(memc_server_config);
memcached_server_t *mem_svr = g_malloc(sizeof(memcached_server_t));
if (NULL == mem_svr)
{
DPRINT(1,NULL,"g_malloc failed \n");
assert(mem_svr);
return NULL;
}
mem_svr->memc_svr_pool = memcached_pool(memc_server_config,size);
if (NULL == mem_svr->memc_svr_pool)
{
DPRINT(1,NULL,"create memcached server pool failed \n");
assert(mem_svr->memc_svr_pool);
g_free(mem_svr);
return NULL;
}
return mem_svr;
}
void destroy_memcached_server(memcached_server_t *memc_server)
{
if (memc_server)
{
memcached_pool_destroy(memc_server->memc_svr_pool);
g_free(memc_server);
}
}
static int memc_pool_release(memcached_server_t *memc_server,memcached_st *mem_s)
{
memcached_return_t rt = memcached_pool_release(memc_server->memc_svr_pool,mem_s);
if (MEMCACHED_SUCCESS != rt)
{
DPRINT(2,NULL,"memcached_pool_release. [%s]\n",memcached_strerror(mem_s,rt));
return -1;
}
return 0;
}
char * memcached_server_get(memcached_server_t *memc_server,const char *key,size_t key_length,size_t *value_length)
{
assert(memc_server);
assert(memc_server->memc_svr_pool);
assert(key);
assert(value_length);
memcached_return_t rt;
memcached_st *mem_s = memcached_pool_fetch(memc_server->memc_svr_pool,NULL,&rt);
if (memcached_failed(rt))
{
DPRINT(2,NULL,"memcached_pool_fetch failed. [%s]\n",memcached_strerror(mem_s,rt));
return NULL;
}
char *value = NULL;
uint32_t flag;
value = memcached_get(mem_s,key,key_length,value_length,&flag,&rt);
if (memcached_failed(rt))
{
DPRINT(6,NULL,"memcached_get failed. [key : %s, reason : %s]\n",key,memcached_strerror(mem_s,rt));
}
memc_pool_release(memc_server,mem_s);
return value; //remember: it must be free by caller
}
测试代码:
host = "127.0.0.1:11220";
memcached_server_t *memcached_server = create_memcached_server(host,strlen(host));
在多线程环境下执行:
thread1:
memcached_server_get(memcached_server,key1,key1_size,&value_size);
thread2
memcached_server_get(memcached_server,key2,key2_size,&value_size);
以上的测试,有个线程获取关键字的值可能会失败。假如thread1正执行在69行代码处(memcached_get),而此时thread2也进入memcached_server_get函数,如果thread2在执行memcached_pool_fetch时,thread1还没有释放资源(memc_pool_release),thread2将会在memcached_pool_fetch处执行失败,从而导致整个程序的错误结果。
因此在理解这些线程安全的接口上的时候,需要万分的注意。线程安全接口,使用的数据结构都是相互独立的,所以我们可以用相互独立的memcached_server_t结构指针,去代替全局的memcached_server。