1. 程式人生 > >Linux執行緒淺析[執行緒的同步和互斥之執行緒互斥鎖]

Linux執行緒淺析[執行緒的同步和互斥之執行緒互斥鎖]

Linux執行緒淺析[執行緒的同步和互斥]

  1. 執行緒同步
  2. 執行緒互斥
  3. 執行緒互斥的相關函式
  4. 執行緒同步的相關函式

執行緒同步

是巨集觀上的一個概念,在微觀上面包含執行緒的相互排斥和執行緒的執行順序的約束問題
解決方法:
    條件變數
    執行緒訊號量

例子:一棟大樓的建造,包含了質監局的檢驗和工程隊的施工,那麼假設質監局和工程隊是兩個執行緒,而大樓則為共享資源,首先相互排斥的原則為工程隊施工的時候,質監局是不能進行檢驗的,因為這個時候大樓還沒有建好,而質監局的檢驗依賴於工程隊施工的結果,所以質監局必須等待工程隊將大樓建造好.所以後一個執行緒依賴於前一個執行緒對共享資源操作的結果.所以說執行緒同步是包含了相互排斥和執行緒的執行順序.

執行緒互斥

執行緒的相互排斥
解決方法:
    互斥鎖
    讀寫鎖
    執行緒訊號量

 例子:
 一個公用廁所,假設只有一個坑,那麼很多人去上廁所的時候,後一個就必須等後一個上完廁所之後才能上廁所.所以同一時間只能有一個人上廁所.這就是互斥.其實在生活中有很多例子.如銀行賬戶的操作問題.12306的售票問題等.都是互斥
 注意:同步包含了互斥,互斥僅僅只是相互排斥,而同步是互斥加順序

 先來一段有問題的程式碼:兩個執行緒同時去訪問同一個賬戶的問題:
  account.h

/*
 * ===========================================================================
 *
 *       Filename:  account.h
 *    Description:  
 *        Version:  1.0
 *        Created:  2017年03月28日 22時04分18秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */
#ifndef __ACCOUNT_H_ #define __ACCOUNT_H_ //宣告銀行賬戶的結構體 typedef struct{ int code; double balance; }Account; //建立銀行賬戶 extern Account* create_account(int code,double balance); //銷燬一個賬戶 extern void destory_account(Account* account); //存款 extern double withdraw(Account* account,double amt); //取款 extern
double deposit(Account* a,double amt); //檢視賬戶餘額 extern double get_balance(Account* account); #endif

account.c

/*
 * ===========================================================================
 *
 *       Filename:  account.c
 *    Description:  
 *        Version:  1.0
 *        Created:  2017年03月30日 21時16分28秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include"account.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>
#include<assert.h>
#include<pthread.h>
/* 
 *建立一個賬戶
 *返回這個賬戶的指標
 *
 * */
extern Account* create_account(int code,double balance){
    Account* account = (Account*)malloc(sizeof(Account));
    assert(account !=NULL);
    account->code = code;
    account->balance = balance;
    //初始化鎖的過程
    return account;
}
/* *
 *銷燬一個賬戶
 * */
extern void destory_account(Account* account){
   assert(account!=NULL);
   free(account);
}

/* *
 *取款,返回的是賬戶的餘額
 *
 * */
extern double withdraw(Account * account,double amt){
  assert(account != NULL);
  if(amt < 0 || amt > account->balance){
    return 0.0;
  }
  int balance = account -> balance;
  sleep(1);
  int result = balance - amt;
  account ->balance = result;
  //pthread_mutex_unlock(&account->pthreadmutex);
  return amt;
}
/* *
 *存款,返回的也是賬戶的餘額
 *
 * */
extern double deposit(Account* account,double amt){
   assert(account != NULL);
   if(amt < 0){
      return 0.0;
   }
   int result = account ->balance + amt;
   sleep(2);
   account->balance = result;
   return result;
}

/* *
 *獲取的就是這個賬戶的餘額
 *
 * */
extern double get_balance(Account* account){
  assert(account !=NULL);
  return account -> balance;
}

accountTest.c

/*
 * ===========================================================================
 *
 *       Filename:  accountTest.c
 *    Description:  
 *        Version:  1.0
 *        Created:  2017年03月30日 21時31分19秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include"account.h"

typedef struct{
  char name[20];
  Account* account;
  double amt;
}ArgvType;

/* *
 *檢查賬戶的函式
 * */
void* th_check(void *argv){
  ArgvType* argvType = (void*)argv;
  double balance = get_balance(argvType->account);
  printf("name:%s,balance:%f,pthread:%lx\n",argvType->name,balance,pthread_self());
  return (void*)0;
}

/* *
 *取款函式
 * */
void* th_withdraw(void *argv){
  ArgvType* argvType = (ArgvType*)argv;
  double balance = withdraw(argvType->account,argvType->amt);
  printf("name:%s,balance:%f,pthread:%lx\n",argvType->name,balance,pthread_self());
  return (void*)0;
}

/* *
 *存款函式
 * */
void* th_deposite(void *argv){
  ArgvType* argvType = (ArgvType*)argv;
  double balance = deposit(argvType->account,argvType->amt);
  printf("name:%s,balance:%f,pthread:%lx\n",argvType->name,balance,pthread_self());
  return (void*)0;
}

int main(int argc,char *argv[]){
  int result;
  pthread_t boyfriend,girlfriend;
  Account *account = create_account(10001,10000);
  ArgvType argv_typeboy,argv_typegirl;
  strcpy(argv_typeboy.name,"zhangsan");
  strcpy(argv_typegirl.name,"xiaoli");
  argv_typeboy.account = account;
  argv_typegirl.account = account;
  argv_typeboy.amt = 10000;
  argv_typegirl.amt = 10000;

  if((result = pthread_create(&boyfriend,NULL,th_withdraw,(void*)&argv_typeboy))!=0){
     perror("boythread create error");
     exit(EXIT_FAILURE);
  }
  if((result = pthread_create(&girlfriend,NULL,th_withdraw,(void*)&argv_typegirl))!=0){
    perror("girlthread create error");
    exit(EXIT_FAILURE);
  }

  pthread_join(boyfriend,NULL);
  pthread_join(girlfriend,NULL);
  printf("get_balance:%f\n",get_balance(account));
  printf("thread:%lx exe ended\n",pthread_self());



  return 0;
}

name:zhangsan,withdraw:10000.000000,balance:0.000000,pthread:7f34bf2a9700
name:xiaoli,withdraw:10000.000000,balance:0.000000,pthread:7f34beaa8700
get_balance:0.000000
thread:7f34bfa94740 exe ended

 輸出的結果如下所示:
 從上面輸出結果中我們可以看到,其實xiaoli和zhangsan都取到了10000,但是這中情況是及其不符合日常生活的邏輯的,而我們的執行緒互斥的目的也就是為了解決這樣一種併發,即多個執行緒訪問同一個共享資源的時候,怎麼去進行處理,後面在此例子基礎上面去做修改.

執行緒互斥的相關函式

互斥鎖:

互斥鎖是一種簡單的加鎖的方法來控制對共享資源的訪問。在同一時刻只能有一個執行緒掌握某個互斥鎖,擁有上鎖狀態的執行緒能夠對共享資源的訪問,若其他執行緒希望上鎖一個已經被上了鎖的資源,則該執行緒掛起,知道上鎖的執行緒釋放互斥鎖為止;

互斥鎖資料型別:pthread_mutex_t

#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *mutexattr);//初始化互斥鎖
int pthread_mutex_destory(pthread_mutex_t * mutex);
//銷燬互斥鎖
返回:成功返回0,失敗返回錯誤編號
引數:
    mutex:互斥鎖
    mutexattr:互斥鎖建立方式
        PTHREAD_MUTEX_INITIALIZER:建立快速互斥鎖
        PTHREAD_RECERSIVE_MUTEX_INITIALIZER_NP:建立遞迴互斥鎖
        PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP:建立檢錯互斥鎖

互斥鎖的加鎖和釋放鎖的過程

#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)
功能:釋放鎖
返回:成功返回0,錯誤返回錯誤碼
引數:mutex
一個共享資源繫結一把鎖,
建議:定義在結構體中,儘量不設定成為全域性變數,否則可能會出現一把鎖去鎖幾百個賬戶,導致併發性的下降:
臨界區:對共享資源進行加鎖到釋放鎖的整個程式碼區域(從上鎖開始到釋放鎖結束)

互斥鎖的屬性建立和銷燬;

    #include<pthread.h>
    int pthread_mutexattr_init(pthread_mutexattr_t *attr);
    int pthread_mutexattr_destory(pthead_muteattr_t *attr);
    返回值:成功返回0,失敗返回錯誤碼

引數:
    attr:互斥鎖屬性

互斥鎖的程序共享屬性:

#include<pthread.h>

int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr,int *restrict pshared)
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,int pshared)
返回:成功返回0,失敗返回錯誤編號
引數:
    attr:互斥鎖屬性
    pshared:程序共享屬性
    PTHREAD_PROCESS_PRIVATE(預設)
        鎖只能用於一個程序內部的兩個執行緒進行互斥
    PTHREAD_PROCESS_SHARED
        鎖能夠用於兩個不同程序中的執行緒進行互斥(跨程序)

一般情況下我們都是在同一個程序中去使用互斥鎖

互斥鎖的型別:

#include<pthread.h>
int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr,int *restrict type)
int pthread_mutexattr_settype(pthread_amutexattr_t *attr,int type)
引數
    attr 互斥鎖屬性
    type:互斥鎖型別
        標準互斥鎖:PTHREAD_MUTEX_NOMAL
            第一次上鎖成功,第二次上鎖失敗
        遞迴互斥鎖:PTHREAD_MUTEX_RECURSIVE
            第一次上鎖成功,第二次上鎖還是成功,內部計數
        檢錯互斥鎖:PTHREAD_MUTEX_ERRORCHECK
            第一次上鎖成功,第二次上錯會出錯
預設互斥鎖:PTHREAD_MUTEX_DEFAULT(同標準互斥鎖)

 先上一個簡單的上鎖的案例:,在上述基礎上修改的

/*
 * ===========================================================================
 *
 *       Filename:  account.h
 *    Description:  
 *        Version:  1.0
 *        Created:  2017年03月28日 22時04分18秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#ifndef __ACCOUNT_H_
#define __ACCOUNT_H_
//宣告銀行賬戶的結構體
//引用pthread標頭檔案.方便後面加鎖
#include<pthread.h>
  typedef struct{
    int code;
    double balance;
    //將鎖宣告在結構體中,有助於每一個這樣的結構體都有一把鎖,那麼在操作的時候,有利於優化併發效果
    pthread_mutex_t pthreadmutex;
  }Account;

//建立銀行賬戶
extern Account* create_account(int code,double balance);

//銷燬一個賬戶
extern void destory_account(Account* account);

//存款
extern double withdraw(Account* account,double amt);

//取款
extern double deposit(Account* a,double amt);

//檢視賬戶餘額
extern double get_balance(Account* account);

#endif
/*
 * ===========================================================================
 *
 *       Filename:  account.c
 *    Description:  
 *        Version:  1.0
 *        Created:  2017年03月30日 21時16分28秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include"account.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>
#include<assert.h>
#include<pthread.h>
/* 
 *建立一個賬戶
 *返回這個賬戶的指標
 *
 * */
extern Account* create_account(int code,double balance){
    Account* account = (Account*)malloc(sizeof(Account));
    assert(account !=NULL);
    account->code = code;
    account->balance = balance;
    //初始化鎖的過程
    pthread_mutex_init(&account->pthreadmutex,NULL);
    return account;
}
/* *
 *銷燬一個賬戶
 * */
extern void destory_account(Account* account){
   assert(account!=NULL);
   free(account);
   //銷燬鎖的過程
   pthread_mutex_destroy(&account->pthreadmutex);
}

/* *
 *取款,返回的是賬戶的餘額
 *
 * */
extern double withdraw(Account * account,double amt){
  assert(account != NULL);
  //上鎖的過程
  pthread_mutex_lock(&account->pthreadmutex);
  if(amt < 0 || amt > account->balance){
    //解鎖的過程
    pthread_mutex_unlock(&account->pthreadmutex);
    return 0.0;
  }
  int balance = account -> balance;
  sleep(1);
  int result = balance - amt;
  account ->balance = result;
  //解鎖的過程
  pthread_mutex_unlock(&account->pthreadmutex);
  return amt;
}
/* *
 *存款,返回的也是賬戶的餘額
 *
 * */
extern double deposit(Account* account,double amt){
   assert(account != NULL);
   //上鎖的過程
   pthread_mutex_lock(&account->pthreadmutex);
   if(amt < 0){
     //解鎖的過程
    pthread_mutex_unlock(&account->pthreadmutex);
      return 0.0;
   }
   int balance = account ->balance;
   sleep(2);
   int result = balance + amt;
   account->balance = result;
   //解鎖的過程
   pthread_mutex_unlock(&account->pthreadmutex);
   return result;
}

/* *
 *獲取的就是這個賬戶的餘額
 *
 * */
extern double get_balance(Account* account){
  assert(account !=NULL);
  return account -> balance;
}
/*
 * ===========================================================================
 *
 *       Filename:  accountTest.c
 *    Description:  
 *        Version:  1.0
 *        Created:  2017年03月30日 21時31分19秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include"account.h"

typedef struct{
  char name[20];
  Account* account;
  double amt;
}ArgvType;

/* *
 *檢查賬戶的函式
 * */
void* th_check(void *argv){
  ArgvType* argvType = (void*)argv;
  double balance = get_balance(argvType->account);
  printf("name:%s,balance:%f,pthread:%lx\n",argvType->name,balance,pthread_self());
  return (void*)0;
}

/* *
 *取款函式
 * */
void* th_withdraw(void *argv){
  ArgvType* argvType = (ArgvType*)argv;
  double balance = withdraw(argvType->account,argvType->amt);
  printf("name:%s,balance:%f,pthread:%lx\n",argvType->name,balance,pthread_self());
  return (void*)0;
}

/* *
 *存款函式
 * */
void* th_deposite(void *argv){
  ArgvType* argvType = (ArgvType*)argv;
  double balance = deposit(argvType->account,argvType->amt);
  printf("name:%s,balance:%f,pthread:%lx\n",argvType->name,balance,pthread_self());
  return (void*)0;
}

int main(int argc,char *argv[]){
  int result;
  pthread_t boyfriend,girlfriend;
  Account *account = create_account(10001,10000);
  ArgvType argv_typeboy,argv_typegirl;
  strcpy(argv_typeboy.name,"zhangsan");
  strcpy(argv_typegirl.name,"xiaoli");
  argv_typeboy.account = account;
  argv_typegirl.account = account;
  argv_typeboy.amt = 10000;
  argv_typegirl.amt = 10000;

  if((result = pthread_create(&boyfriend,NULL,th_withdraw,(void*)&argv_typeboy))!=0){
     perror("boythread create error");
     exit(EXIT_FAILURE);
  }
  if((result = pthread_create(&girlfriend,NULL,th_withdraw,(void*)&argv_typegirl))!=0){
    perror("girlthread create error");
    exit(EXIT_FAILURE);
  }

  pthread_join(boyfriend,NULL);
  pthread_join(girlfriend,NULL);
  printf("get_balance:%f\n",get_balance(account));
  printf("thread:%lx exe ended\n",pthread_self());



  return 0;
}

這裡寫圖片描述
 輸出的結果如上所示,這個時候其實取款的時候就已經沒有併發情況再出現了

/*
 * ===========================================================================
 *
 *       Filename:  account.c
 *    Description:  
 *        Version:  1.0
 *        Created:  2017年03月30日 21時16分28秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include"account.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>
#include<assert.h>
#include<pthread.h>

static pthread_mutexattr_t attr;
/* 
 *建立一個賬戶
 *返回這個賬戶的指標
 *
 * */
extern Account* create_account(int code,double balance){
    Account* account = (Account*)malloc(sizeof(Account));
    assert(account !=NULL);
    account->code = code;
    account->balance = balance;
    //初始化鎖的過程
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_NORMAL);
    //pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
    //pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ERRORCHECK);
    //pthread_mutex_init(&account->pthreadmutex,NULL);
    pthread_mutex_init(&account->pthreadmutex,&attr);
    return account;
}
/* *
 *銷燬一個賬戶
 * */
extern void destory_account(Account* account){
   assert(account!=NULL);
   free(account);
   //銷燬鎖的過程
   pthread_mutexattr_destroy(&attr);
   pthread_mutex_destroy(&account->pthreadmutex);
}

/* *
 *取款,返回的是賬戶的餘額
 *
 * */
extern double withdraw(Account * account,double amt){
  assert(account != NULL);
  //上鎖的過程
  pthread_mutex_lock(&account->pthreadmutex);
  if(amt < 0 || amt > account->balance){
    //解鎖的過程
    pthread_mutex_unlock(&account->pthreadmutex);
    return 0.0;
  }
  int balance = account -> balance;
  sleep(1);
  int result = balance - amt;
  account ->balance = result;
  //解鎖的過程
  pthread_mutex_unlock(&account->pthreadmutex);
  return amt;
}
/* *
 *存款,返回的也是賬戶的餘額
 *
 * */
extern double deposit(Account* account,double amt){
   assert(account != NULL);
   //上鎖的過程
   pthread_mutex_lock(&account->pthreadmutex);
   if(amt < 0){
     //解鎖的過程
    pthread_mutex_unlock(&account->pthreadmutex);
      return 0.0;
   }
   int balance = account ->balance;
   sleep(2);
   int result = balance + amt;
   account->balance = result;
   //解鎖的過程
   pthread_mutex_unlock(&account->pthreadmutex);
   return result;
}

/* *
 *獲取的就是這個賬戶的餘額
 *
 * */
extern double get_balance(Account* account){
  assert(account !=NULL);
  return account -> balance;
}

 以上都是對c的pthread互斥鎖做的一個相對比較簡單的一些使用方法.有興趣的可以copy下來run一下,因為我也是新手,寫的不好的地方歡迎指出