1.初始化与销毁互斥量
Linux使用pthread_mutex_t 数据类型表示互斥量,并使用pthread_mutex_init函数对互斥量进行初始化。
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t * restrict mutex,const pthread_mutexattr * restrict attr);
第一个参数是互斥量的指针,互斥量在该函数内被初始化。第二个参数是互斥量的属性,一般置为NULL。
Linux还提供了另一种初始化互斥量的方法,用户可以将互斥量设置为PTHREAD_MUTEX_INITIALIZER,这种方法有个局限性,那就是如果互斥量是使用动态分配内存的方法得到的,那么就不能使用如下方法初始化信号量。
pthread_mutex_t * mutex;
mutex=(pthead_mutex_t * )malloc(sizeof(pthread_mutex_t));
mutex=PTHREAD_MUTEX_INITIALIZER;
以上的初始化是错误的。原因在于linux将pthread_mutex_t 类型定义为结构体,而PTHREAD_MUTE_INITIALIZER常量相当于已经设置好的结构体变量中每一个成员变量的值。显而易见,一个已经定义的结构体对象可以使用这种方法,而一个利用malloc得来的结构体对象不能够使用这种方法。
如果要使用PTHREAD_MUTEX_INITIALIZER,可以这样用。
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
当一个互斥量不再使用时,使用如下函数用如下函数将其销毁。
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t * mutex);
2.得到与释放互斥量
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t * mutex);
int pthread_mutex_trylock(pthread_mutex_t * mutex);
int pthread_mutex_unlock(pthread_mutex_t * mutex);
第一个函数在所要求的互斥量的锁已被其他线程所取得的情况下,会阻塞调用线程。第二个函数不会阻塞调用线程,会立即返回EBUSY。表示锁处于繁忙状态。
用第二个函数实现一个非阻塞的加锁函数版本,用于忙等待一个互斥量的锁。
……
while(pthread_mutex_trylock(&mutex)==EBUSY);
……
3.实例
该程序维护一个任务队列,其本质是一个链表。两个线程扫描链表,将属于自己的任务从任务列表中摘下。
两个线程分别对链表进程读操作。为了防止可中断的读写之间造成的数据不一致的错误,使用互斥量。
//mutex_list.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#define MAX_ITEM 3 /* 每次最多取三个任务 */
typedef struct job * Job;
/* 链表节点结构 */
struct job{
pthread_t tid;
Job next;
int val;
};
pthread_mutex_t q_lock = PTHREAD_MUTEX_INITIALIZER; /* 全局变量锁 */
int insert(Job *head, int val, pthread_t tid)
{
Job p, q;
p = *head; /* 头指针 */
if(p != NULL){ /* 判断空链表的情况 */
while(p->next != NULL){
p = p->next;
}
}
q = (struct job *)malloc(sizeof(struct job));
if(q == NULL){
perror("fail to malloc");
return -1;
}
q->next = NULL;
q->val = val;
q->tid = tid;
p->next = q;
if(p == NULL){ /* 设置链表头 */
*head = q;
return 0;
}
p->next = q;
return 0;
}
void get_job(Job head, Job task, int *count)
{
struct job *p, *q;
pthread_t tid;
q = head;
p = q->next;
tid = pthread_self();
while(p != NULL){
if(tid == p->tid){
q->next = p->next;
p->next = task;
task = p;
p = q->next;
*count++;
}else{
q = p;
p = p->next;
}
}
}
int free_job(Job head)
{
Job p, q;
for(p = head; p != NULL; p = p->next){
q = p;
p = p->next;
free(q);
}
return 0;
}
void print(Job head)
{
Job p;
for(p = head; p != NULL; p = p->next)
printf("thread %u: %d/n", p->tid, p->val);
}
void * tfn7(void * arg)
{
int count;
struct job * task = NULL;
pthread_t tid;
tid = pthread_self();
count = 0;
while(count < MAX_ITEM)
if(pthread_mutex_trylock(&q_lock) == 0){
get_job((Job) arg, task, &count);
pthread_mutex_unlock(&q_lock);
}
print((Job) arg);
if(free_job(task) == -1)
exit(1);
return (void *)0;
}
int main(void)
{
struct job * item;
pthread_t tid1, tid2;
int i;
int err;
item = (struct job *)malloc(sizeof(struct job));
item->next = NULL;
item->val = 0;
item->tid = -1;
if((err = pthread_create(&tid1, NULL, tfn7, item)) != 0){
printf("fail to create thread %s/n", strerror(err));
exit(0);
}
if((err = pthread_create(&tid2, NULL, tfn7, item)) != 0){
printf("fail to create thread %s/n", strerror(err));
exit(0);
}
printf("===the 1st put===/n");
pthread_mutex_lock(&q_lock);
for(i = 0; i < 2; i++){
if(insert(&item, i, tid1) == -1)
exit(1);
if(insert(&item, i + 1, tid2) == -1)
exit(1);
}
if(insert(&item, 10, tid1) == -1)
exit(1);
pthread_mutex_unlock(&q_lock);
sleep(5);
/* 休眠,保证线程可以取走任务节点,在这里不能使用pthread_join函数
*因为队列中只有两个节点属于线程2,未取走3个节点线程是不会退出的
*所以pthread_join函数会导致死锁
*/
printf("===the 2nd put===/n");
pthread_mutex_lock(&q_lock);
if(insert(&item, 9, tid2) == -1)
exit(1);
pthread_mutex_unlock(&q_lock);
err = pthread_join(tid1, NULL);
if(err != 0){
printf("can¡¯t join thread %s/n", strerror(err));
exit(1);
}
err = pthread_join(tid2, NULL);
if(err != 0){
printf("can¡¯t join thread %s/n", strerror(err));
exit(1);
}
printf("main thread done/n");
if(item->next == NULL)
printf("No job in the queue/n");
free(item);
return 0;
}