接触Unix环境的posix线程有一段时间了,始终有些问题莫名其妙,比如互斥锁……
在Windows下就发现有这样的现象:
共享的数据:
int a = 0;
//线程1执行:
EnterCriticalSection(&lock);
for(;;){
cout<<a<<endl;
Sleep(100);
}
LeaveCriticalSection(&lock);
//线程2执行:
for(;;)
a++;
程序输出中a被线程2修改,线程1连续打印递增的结果。不过当时没管他。
最近在Linux下写一个线程池,可是遇到一个问题解决不了,总是卡死,朋友说是我逻辑上有问题,于是花心思写了一些测试程序,就是想搞清楚互斥锁。我又把上面的情况移到Linux下,看到一样的情况。这下麻烦了,所有的资料都同一个说法:互斥锁(或临界区)是用来保护共享内存的。但是这种情况下怎么保护?没有资料来解释一下。
后来做了这样的测试:
pthread_mutex_t locker = /*初始化锁*/;
void* thread_proc(void *)
{
//线程函数体
printf("thread %d sleep...\n",arg);
pthread_mutex_lock(&lock);/*这里锁定*/
sleep(1); //什么也不干,just sleep
pthread_mutex_unlock(&lock);//*这里解锁*/
printf("thread %d wake up",arg);//提示线程已经解锁
... //这里不重要
return NULL;
}
在主函数里创建5个线程,打印结果比较有趣,信号量设置初始资源为1时的情况相似:
把printf("thread %d sleep...\n",arg);放到pthread_mutex_lock(&lock);后面,就是这样:
有时有交错:
看来线程是在pthread_mutex_lock(&lock);这里阻塞了,只等pthread_mutex_unlock(&lock);之后才放行。初步分析,这些互斥锁的作用原理就好比一扇门,一次只能一个线程放行。而且和共享的内存没任何关系。到FreeBSD下重新编译一下,结果一样,我就不贴出来了。
感觉被误导了,不知道我这样分析对不对,作用原理应该就是一个通道有两个门,一个进一个出,通道里是操作共享数据的地方,只能有一个线程在通道里;而信号量可以让多个线程同时在通道里,出一个,才能进一个。如果我理解的正确,那我应该注意两点:尽量别把情况搞复杂了,不然卡住了就不好分析了;一个锁不要用在两个地方。
下面是最后的线程池代码:
声明:threadpool.h
#ifndef TP_H_
#define TP_H_
#include <stdlib.h>
#include <sys/types.h>
#include <pthread.h>
/****************************************/
/*task and task queue*/
typedef struct tp_task
{
void *data;
void (*operate)(void *);
struct tp_task *next;
}tp_task;
// operators of task struct
tp_task* new_task(void (*fun)(void *), void *dat);
// task struct queue
typedef struct tp_task_que
{
tp_task *head;
tp_task *tail;
int size;
}tp_task_que;
// operators of task queue struct
int push_task( tp_task_que *tq, tp_task *pt);
tp_task* pop_task(tp_task_que *tq);
void clear_task_que(tp_task_que *tq);
/*********************************************/
/*线程池*/
typedef struct thread_info_str thread_str;
typedef struct thread_pool_str th_pool_str;
// thread struct
struct thread_info_str
{
//用于循环控制
pthread_cond_t loop_cond;
pthread_mutex_t loop_lock;
//用来同步task_que的操作
pthread_mutex_t task_lock;
tp_task_que task_que;
pthread_t t_id;
th_pool_str *p_tpool;
int is_free;
};
// pool information
struct thread_pool_str
{
//同步线程和主线程
pthread_cond_t tp_cond;
pthread_mutex_t tp_lock;
int wait_for_ready;
//线程结构体数组
thread_str *th_arr;
int th_num;
int index;
};
/*thread函数*/
void* thread_proc(void *arg);
/*threadpool接口*/
int init_tpool(th_pool_str *tpool, int num);
void close_tpool(th_pool_str *tpool);
int submit_task(th_pool_str *tpool, void (*operate)(void *), void *data );
#endif
实现:threaadpool.c
#include <stdio.h>
#include <signal.h>
#include <assert.h>
#include "threadpool.h"
#ifndef NDEBUG
#define PRTDEBUG(str) puts(str)
#else
#define PRTDEBUG(str)
#endif
/***task queue***/
// create a new task
tp_task* new_task(void (*op)(void *), void *dat)
{
if (NULL!=op)
{
tp_task *pt = (tp_task *)malloc(sizeof(tp_task));
if (NULL!=pt)
{
pt->data = dat;
pt->operate = op;
pt->next = NULL;
return pt;
}
}
return NULL;
}
// push node at tail
int push_task( tp_task_que *tq, tp_task *pt)
{
assert(NULL!=tq);
if (0==tq->size)
{
tq->head = tq->tail = pt;
tq->size = 1;
}
else
{
tq->tail->next = pt;
tq->tail = pt;
tq->size++;
}
return 0;
}
// pop node at head
tp_task* pop_task(tp_task_que *tq)
{
assert(NULL!=tq);
tp_task *tmp;
if (0==tq->size)
{
return NULL;
}
else
{
tmp = tq->head;
tq->head = tmp->next;
tq->size--;
if (0==tq->size)
{
tq->tail = NULL;
}
return tmp;
}
}
// clear task queue
void clear_task_que(tp_task_que *tq)
{
assert(NULL!=tq);
if (NULL!=tq && tq->size>0)
{
tp_task *tmp1 = tq->head, *tmp2;
do
{
tmp2 = tmp1;
tmp1 = tmp1->next;
free(tmp2);
} while (NULL!=tmp1);
tq->size = 0;
tq->head = NULL;
tq->tail = NULL;
}
}
///
// thread process
void* thread_proc(void *arg)
{
thread_str *th = (thread_str *)arg;
th_pool_str *tpool = th->p_tpool;
pthread_mutex_lock(&tpool->tp_lock);
tpool->wait_for_ready = 0;
pthread_cond_signal(&tpool->tp_cond);
pthread_mutex_unlock(&tpool->tp_lock);
tp_task *tmp;
while (1)
{
// loop waitinng task
pthread_mutex_lock(&th->loop_lock);
if (0==(th->task_que.size))
pthread_cond_wait(&th->loop_cond, &th->loop_lock);
pthread_mutex_unlock(&th->loop_lock);
PRTDEBUG("actived");
// loop doing task
th->is_free = 0;
for (;;)
{
pthread_mutex_lock(&th->task_lock);
tmp = pop_task(&th->task_que);
pthread_mutex_unlock(&th->task_lock);
if (NULL==tmp)
break;
(tmp->operate)(tmp->data);
}
th->is_free = 1;
}
pthread_exit(0);
}
// initial pool
int init_tpool(th_pool_str *tpool, int num)
{
if (0!=pthread_cond_init(&tpool->tp_cond, NULL))
{
fputs("pthread_cond_init", stderr);
return -1;
}
if (0!=pthread_mutex_init(&tpool->tp_lock, NULL))
{
fputs("pthread_mutex_init", stderr);
return -1;
}
thread_str *th_arr = (thread_str *)malloc(num * sizeof(thread_str));
if (NULL==th_arr)
{
fputs("malloc", stderr);
return -1;
}
int i, ret;
for (i = 0; i < num; i++)
{
th_arr[i].task_que.head = NULL;
th_arr[i].task_que.tail = NULL;
th_arr[i].task_que.size = 0;
ret = (pthread_cond_init(&th_arr[i].loop_cond, NULL) != 0) ||
(pthread_mutex_init(&th_arr[i].loop_lock, NULL) != 0) ||
(pthread_mutex_init(&th_arr[i].task_lock, NULL) != 0);
if (ret)
{
fputs("thread_str initial", stderr);
break;
}
if (pthread_create(&th_arr[i].t_id, NULL, thread_proc, &th_arr[i]))
{
perror("pthread_create");
break;
}
th_arr[i].is_free = 0;
th_arr[i].p_tpool = tpool;
tpool->wait_for_ready = 1;
pthread_mutex_lock(&tpool->tp_lock);
if (tpool->wait_for_ready)
pthread_cond_wait(&tpool->tp_cond, &tpool->tp_lock);
pthread_mutex_unlock(&tpool->tp_lock);
}
if (i<num)
{
for (; i>=0; --i)
{
pthread_kill(th_arr[i].t_id,SIGKILL);
}
free(th_arr);
return -1;
}
tpool->th_arr = th_arr;
tpool->th_num = num;
tpool->index = 0;
return 0;
}
// close all threads of pools
void close_tpool(th_pool_str *tpool)
{
int i;
for (i = 0; i < (tpool->th_num); i++)
{
pthread_kill(tpool->th_arr[i].t_id, SIGKILL);
clear_task_que(&tpool->th_arr[i].task_que);
}
free(tpool->th_arr);
PRTDEBUG("destroy thread pool\n");
}
// submit one task to thread pool
int submit_task(th_pool_str *tpool, void (*operate)(void *), void *data )
{
if (NULL==tpool)
return -1;
thread_str *th = &tpool->th_arr[tpool->index];
int ret;
tp_task *ptask = new_task(operate, data);
pthread_mutex_lock(&th->task_lock);
ret = push_task(&th->task_que, ptask);
pthread_mutex_unlock(&th->task_lock);
if (ret)
return -1;
pthread_mutex_lock(&th->loop_lock);
pthread_cond_signal(&th->loop_cond);
pthread_mutex_unlock(&th->loop_lock);
tpool->index++;
if ( (tpool->index) >= (tpool->th_num) )
tpool->index = 0;
return 0;
}
使用测试文件:test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "threadpool.h"
void fun(void *arg)
{
printf("task %d start...\n",arg);
sleep(1);
printf("task %d complite!\n",arg);
}
int main (int argc, char *argv[])
{
long i,n,m;
scanf("%d%d",&n,&m);
getchar();
th_pool_str tpool;
if (init_tpool(&tpool, n))
{
fputs("init_tpool",stderr);
_exit(1);
}
printf("create %d thread in pool\n",i);
for (i = 1; i <= m; i++)
{
submit_task(&tpool, fun, (void*)i);
}
printf("submit end\n");
getchar();
puts("at the end\n");
close_tpool(&tpool);
_exit(0);
}
在Linux和FreeBSD下都通过并正常运行。
使用方法只有三个函数,功能很少,但是方便的。