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;
}