【ARM&Linux】常用執行緒同步的三種方法
【執行緒同步高效率程式設計】
Linux系統中執行緒最大的特點就是共享性,執行緒同步問題較為困難也很重要,最常用的三種是:條件變數、互斥鎖、無名訊號量。(ps: 有名訊號量可用於程序同步,無名訊號量只能用於執行緒同步,是輕量級的。)
(一)、【互斥鎖】:mutex
執行緒互斥量資料型別:
pthread_mutex_t
初始化鎖
靜態分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
動態分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
引數一:建立的互斥鎖
引數二:儲存互斥鎖資訊的結構,一般為NULL加鎖:對共享資源的訪問,要對互斥量進行加鎖,如果互斥量已經上了鎖,呼叫執行緒會阻塞,直到互斥量被解鎖。
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
引數:指明互斥鎖解鎖:在完成了對共享資源的訪問後,要對互斥量進行解鎖
int pthread_mutex_unlock(pthread_mutex_t *mutex);
引數:指明互斥鎖銷燬鎖
int pthread_mutex_destroy(pthread_mutex *mutex);
引數:指明互斥鎖
(二)、【條件變數】:cond
條件變數用來自動阻塞一個執行緒,直到某特殊情況發生為止。通常條件變數和互斥鎖同時使用。
資料型別:pthread_cond_t
。
- 初始化
靜態:pthread_cond_t cond = PTHREAD_COND_INITIALIER;
動態:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
引數一:指明條件變數
引數二:儲存條件變數屬性的結構>
- 等待條件成立:釋放鎖,同時等待條件為真才能有停止阻塞。
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
引數一:指明條件變數
引數二:指明互斥鎖
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
- 啟用條件變數:
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有執行緒的阻塞
- 登出條件變數
int pthread_cond_destroy(pthread_cond_t *cond);
(三)、【無名訊號量】:sem,
注意:連結需要加上-pthread選項
例如:gcc -pthread main.c -o main
有名訊號量可用於程序的同步,標頭檔案:#include<sys/sem.h>,
;而無名訊號量只能用於執行緒,是輕量級,標頭檔案:#include <semaphore.h>)
。
- 初始化:
int sem_init (sem_t *sem , int pshared, unsigned int value);
引數一:指明訊號量
引數二:共享選項(linux 只支援為0,即表示它是當前程序的區域性訊號量)
引數三:設定初始值
- 等待訊號量:給訊號量減1,然後等待直到訊號量的值大於0。
int sem_wait(sem_t *sem);
- 釋放訊號量:
int sem_post(sem_t *sem);
- 銷燬訊號量:
int sem_destroy(sem_t *sem);
【DEMO】
現有兩個同學打掃衛生,學生A負責掃地,學生B負責拖地,很明顯要掃完地之後在拖地,由此引入執行緒同步。
1、條件變數和互斥鎖的聯合利用實現
#include <stdio.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define DEBUG_INFO(...) printf("Info: "); printf(__VA_ARGS__)
void *student_1();
void *student_2();
int num = 0; //共享資源
pthread_mutex_t mulock = PTHREAD_MUTEX_INITIALIZER; //互斥鎖
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //條件變數
pthread_t stu_thread[2]; //兩個學生執行緒
int main()
{
int i;
// 建立兩個學生執行緒
pthread_create(&stu_thread[0], NULL, student_1, NULL);
pthread_create(&stu_thread[1], NULL, student_2, NULL);
// 等待兩個執行緒結束
for(i=0; i<2; i++)
{
pthread_join(stu_thread[i], NULL);
}
// 登出操作
pthread_mutex_destroy(&mulock);
pthread_cond_destroy(&cond);
return 0;
}
void *student_1()
{
int i;
DEBUG_INFO("student a start work .\n");
for(i=0; i<5; i++)
{
DEBUG_INFO("i = %d \n", i);
pthread_mutex_lock(&mulock); //鎖住
num++; //掃一次地
if(num >= 5)
{
DEBUG_INFO("student a finished work .\n");
pthread_cond_signal(&cond); //通知學生B已經掃完地了,並解鎖
}
pthread_mutex_unlock(&mulock);
sleep(1);
}
pthread_exit(NULL);
return 0;
}
void *student_2()
{
DEBUG_INFO("in student 2 .. \n");
while(num < 5) //不用if四因為需要防止莫名錯誤
{
pthread_cond_wait(&cond, &mulock); //等待學生A掃地結束,等不到會再次一直阻塞
}
num = 0; //拖地
pthread_mutex_unlock(&mulock);
DEBUG_INFO("student b finished work .\n");
pthread_exit(NULL);
return 0;
}
Makefile
執行結果
由執行結果可見,學生A先完成工作,學生B在完成工作,所以成功實現執行緒同步。
2、訊號量實現
/****************************************************************************************
* 檔名: demo2.c
* 建立者:
* 時 間:
* 聯 系:
* 簡 介:
*****************************************************************************************/
#include <stdio.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define DEBUG_INFO(...) printf("Info: "); printf(__VA_ARGS__)
int num = 0; //共享資源
sem_t mysem; //用於同步的訊號量
pthread_t stu_thread[2]; //兩個學生執行緒
void *student_1();
void *student_2();
int main()
{
// 初始化訊號量
sem_init(&mysem, 0, 0);
int i;
// 建立兩個學生執行緒
pthread_create(&stu_thread[0], NULL, student_1, NULL);
pthread_create(&stu_thread[1], NULL, student_2, NULL);
// 等待兩個執行緒結束
for(i=0; i<2; i++)
{
pthread_join(stu_thread[i], NULL);
}
// 登出操作
sem_destroy(&mysem);
return 0;
}
void *student_1()
{
int i;
DEBUG_INFO("student a start work .\n");
for(i=0; i<5; i++)
{
DEBUG_INFO("i = %d \n", i);
num++; //掃一次地
if(num >= 5)
{
DEBUG_INFO("student a finished work .\n");
sem_post(&mysem); //釋放訊號量
}
sleep(1);
}
pthread_exit(NULL);
return 0;
}
void *student_2()
{
DEBUG_INFO("in student 2 .. \n");
sem_wait(&mysem); //等待訊號量
num = 0; //拖地
DEBUG_INFO("student b finished work .\n");
pthread_exit(NULL);
return 0;
}
【執行結果】
由執行結果可見,用訊號量的程式執行結果與使用條件變數結果一致,所以實驗成功!
end..