什麼是互斥量
互斥量是一種用於多線程中的同步訪問的方法,它允許程序鎖住某個對象或者某段代碼,使得每次只能有一個線程訪問它。為了控制對關鍵對象或者代碼的訪問,必須在進入這段代碼之前鎖住一個互斥量,然後在完成操作之後解鎖。其本質和信號量一樣,都是P/V操作。
互斥量相關API
對於互斥量的學習,也離不開各種的API的學習,在開始編碼實現一個小的DEMO前,先來了解一下這些API函數。
/**
* 初始化一個互斥量
* @param mutex* 最終初始化得到的互斥量
* @param mutexattr* 互斥量的屬性
* @return 成功時返回0;否則返回非0錯誤代碼,但是並不設置errno的錯誤值
*/
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
/**
* 互斥量進行加鎖操作
* @param mutex* 由pthread_mutex_init得到的互斥量
* @return 成功時返回0;否則返回非0錯誤代碼,但是並不設置errno的錯誤值
*/
int pthread_mutex_lock(pthread_mutex_t *mutex);
/**
* 互斥量進行解鎖操作
* @param mutex* 由pthread_mutex_init得到的互斥量
* @return 成功時返回0;否則返回非0錯誤代碼,但是並不設置errno的錯誤值
*/
int pthread_mutex_unlock(pthread_mutex_t *mutex);
/**
* pthread_mutex_lock函數的非阻塞版本。
* 如果mutex參數所指定的互斥鎖已經被鎖定的話,
* 調用pthread_mutex_trylock函數不會阻塞當前線程,而是立即返回一個值來描述互斥鎖的狀況。
* @param mutex 由pthread_mutex_init得到的互斥量
* @return 成功時返回0;否則返回非0錯誤代碼,但是並不設置errno的錯誤值
*/
int pthread_mutex_trylock(pthread_mutex_t *mutex);
/**
* 銷毀建立的互斥量
* @param mutex* 由pthread_mutex_init得到的互斥量
* @return 成功時返回0;否則返回非0錯誤代碼,但是並不設置errno的錯誤值
*/
int pthread_mutex_destroy(pthread_mutex_t *mutex);
一個小實例
好了,API函數的介紹總是那麼的干巴巴,來點代碼干貨,看看到底如何使用互斥量。接下來我要編碼實現以下任務:
•主線程可以不斷輸入字母,當輸入end時,終止程序;
•子線程將主線程輸入的字母轉換成大寫;
主線程可以向標准輸入不斷的輸入,子線程可以不斷的進行轉換。不啰嗦,直接上代碼,代碼稍長,請君仔細閱讀。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
void *threadFunc(void *arg);
pthread_mutex_t work_mutex;
sem_t sem;
#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int main()
{
int res;
pthread_t thread1;
void *threadRes;
res = pthread_mutex_init(&work_mutex, NULL); // 初始化互斥量
if (res != 0)
{
perror("Mutex initialization failed.");
exit(EXIT_FAILURE);
}
res = sem_init(&sem, 0, 0);
if (res != 0)
{
perror("Semaphore create failed.");
exit(EXIT_FAILURE);
}
res = pthread_create(&thread1, NULL, threadFunc, NULL);
if (res != 0)
{
perror("Thread create failed.");
exit(EXIT_FAILURE);
}
printf("Input some text. Enter 'end' to finish.\n");
pthread_mutex_lock(&work_mutex);
while (strncmp(work_area, "end", 3) != 0)
{
fgets(work_area, WORK_SIZE, stdin);
pthread_mutex_unlock(&work_mutex);
sem_wait(&sem); // 告訴線程1,我數據讀完了,輪到你去處理了,處理完了再告訴我
pthread_mutex_lock(&work_mutex);
}
pthread_mutex_unlock(&work_mutex);
printf("\nWaiting for thread to finish...\n");
res = pthread_join(thread1, &threadRes);
if (res != 0)
{
perror("Thread join failed.");
exit(EXIT_FAILURE);
}
printf("Thread joined.\n");
pthread_mutex_destroy(&work_mutex);
sem_destroy(&sem);
exit(EXIT_SUCCESS);
}
void *threadFunc(void *arg)
{
pthread_mutex_lock(&work_mutex);
while (strncmp(work_area, "end", 3) != 0)
{
if (work_area[0] != '\0')
{
for (int i = 0; work_area[i] != '\0'; ++i)
{
if (work_area[i] >= 'a' && work_area[i] <= 'z')
{
work_area[i] -= 'a' - 'A';
}
}
printf("After converted: %s\n", work_area);
sem_post(&sem); // 告訴主線程,我處理完了,輪到你再讀取數據了
memset(work_area, 0, sizeof(work_area));
}
pthread_mutex_unlock(&work_mutex);
sleep(1);
pthread_mutex_lock(&work_mutex);
}
pthread_mutex_unlock(&work_mutex);
sem_post(&sem);
pthread_exit(NULL);
}
任務完成了,還算不錯。但是多線程開發中保證同步還是非常難的,別看上面的代碼完成的任務很簡單,但是坑很多,就是現在,我都無法保證上面的代碼100%沒有坑,就等你來挑刺了。
為什麼多線程同步就這麼難呢?直觀上來分析就是,人的大腦是單線程的,如果讓你去多線程的想多件事情,你也會混亂。就好比,讓你左手畫圓,右手畫方,你能畫的很完美麼?道理很簡單,多思考,多寫,多填坑吧。伙計們。