1. 程式人生 > >三十九、Linux 執行緒——執行緒的同步和互斥

三十九、Linux 執行緒——執行緒的同步和互斥

39.1 概念

  • 執行緒同步
    • 是一個巨集觀概念,在微觀上包含執行緒的相互排斥和執行緒先後執行的約束問題
    • 解決同步方式
      • 條件變數
      • 執行緒訊號量
  • 執行緒互斥
    • 執行緒執行的相互排斥
    • 解決互斥的方式
      • 互斥鎖
      • 讀寫鎖
      • 執行緒訊號量

39.2 案例

  

atm_account.c

 1 #include "atm_account.h"
 2 
 3 /** 建立賬戶 */
 4 atm_Account *atm_account_Create(int
code, double balance) 5 { 6 atm_Account *account = (atm_Account *)malloc(sizeof(atm_Account)); 7 if(NULL == account) { 8 return NULL; 9 } 10 11 account->code = code; 12 account->balance = balance; 13 14 return account; 15 } 16 17 /** 銷燬賬戶 */ 18 void atm_account_Destroy(atm_Account *account)
19 { 20 if(NULL == account){ 21 return ; 22 } 23 24 free(account); 25 } 26 27 /** 取款: 成功,則返回取款金額 */ 28 double atm_account_Withdraw(atm_Account *account, double amt) 29 { 30 if(NULL == account) { 31 return 0.0; 32 } 33 34 if(amt < 0 || amt > account->balance) {
35 return 0.0; 36 } 37 38 double balance_tmp = account->balance; 39 sleep(1); 40 balance_tmp -= amt; 41 account->balance = balance_tmp; 42 43 return amt; 44 } 45 46 /** 存款: 返回存款的金額 */ 47 double atm_account_Desposit(atm_Account *account, double amt) 48 { 49 if(NULL == account){ 50 return 0.0; 51 } 52 if(amt < 0){ 53 return 0.0; 54 } 55 56 double balance_tmp = account->balance; 57 sleep(1); 58 balance_tmp += amt; 59 account->balance = balance_tmp; 60 61 return amt; 62 } 63 64 /** 檢視賬戶餘額 */ 65 double atm_account_BalanceGet(atm_Account *account) 66 { 67 if(NULL == account){ 68 return 0.0; 69 } 70 71 double balance_tmp = account->balance; 72 return balance_tmp; 73 }

atm_account.h

 1 #ifndef __ATM_ACCOUNT_H__
 2 #define __ATM_ACCOUNT_H__
 3 
 4 #include <math.h>
 5 #include <malloc.h>
 6 #include <stdlib.h>
 7 #include <stdio.h>
 8 #include <string.h>
 9 #include <unistd.h>
10 
11 /** 賬戶資訊 */
12 typedef struct {
13     int         code;       ///< 銀行賬戶的編碼
14     double      balance;    ///< 賬戶餘額
15 }atm_Account;
16 
17 /** 建立賬戶 */
18 extern atm_Account *atm_account_Create(int code, double balance);
19 /** 銷燬賬戶 */
20 extern void atm_account_Destroy(atm_Account *account);
21 /** 取款 */
22 extern double atm_account_Withdraw(atm_Account *account, double amt);
23 /** 存款 */
24 extern double atm_account_Desposit(atm_Account *account, double amt);
25 /** 檢視賬戶餘額 */
26 extern double atm_account_BalanceGet(atm_Account *account);
27 
28 #endif

atm_handler.c

 1 #include "atm_handler.h"
 2 
 3 /** 定義取款操作的執行緒執行函式 */
 4 void *atm_handler_Withdraw(void *arg)
 5 {
 6     atm_handler_t *handler_tmp = (atm_handler_t *)arg;
 7     double amt = atm_account_Withdraw(handler_tmp->account, handler_tmp->amt);
 8 
 9     printf("%10s(0x%lu) withdraw %f from account %d\n", handler_tmp->name, pthread_self(), amt, handler_tmp->account->code);
10 
11    return (void *)0;
12 }
13 
14 /** 定義存款操作的執行緒執行函式 */
15 void *atm_handler_Desposit(void *arg)
16 {
17     atm_handler_t *handler_tmp = (atm_handler_t *)arg;
18     double amt = atm_account_Desposit(handler_tmp->account, handler_tmp->amt);
19 
20     printf("%10s(0x%lu) deposit %f from account %d\n", handler_tmp->name, pthread_self(), amt, handler_tmp->account->code);
21 
22    return (void *)0;
23 }
24 
25 /** 定義檢查銀行賬戶的執行緒執行函式 */
26 void *atm_handler_AccountCheck(void *arg)
27 {
28     return (void *)0;
29 }
30 
31 /** 賬戶操作主函式 */
32 atm_error_t atm_handler_main(void)
33 {
34     int err;
35     pthread_t boy, girl;
36     atm_Account *account = atm_account_Create(1000001, 10000);
37     if(NULL == account){
38         return ATM_ERROR_ACCOUNT_CREATE;
39     }
40 
41     atm_handler_t usr1, usr2;
42     strcpy(usr1.name, "boy");
43     usr1.account = account;
44     usr1.amt = 10000;
45 
46     strcpy(usr2.name, "girl");
47     usr2.account = account;
48     usr2.amt = 10000;
49 
50     /** 啟動兩個執行緒(boy 和 girl 執行緒)同時去操作同一個銀行賬戶 */
51     if((err = pthread_create(&boy, NULL, atm_handler_Withdraw, (void *)&usr1)) != 0) {
52         perror("pthread create error");
53     }
54 
55     if((err = pthread_create(&girl, NULL, atm_handler_Withdraw, (void *)&usr2)) != 0) {
56         perror("pthread create error");
57     }
58     
59     /** 主執行緒阻塞 */
60     pthread_join(boy, NULL);
61     pthread_join(girl, NULL);
62 
63     printf("account balance: %f\n", atm_account_BalanceGet(account));
64     atm_account_Destroy(account);
65 
66     return ATM_ERROR_NONE;
67 }

atm_handler.h

 1 #ifndef __ATM_HANDLER_H__
 2 #define __ATM_HANDLER_H__
 3 
 4 #include "atm_account.h"
 5 #include <pthread.h>
 6 #include <stdio.h>
 7 #include <stdlib.h>
 8 #include <string.h>
 9 
10 typedef enum {
11     ATM_ERROR_NONE,
12     ATM_ERROR_ACCOUNT_CREATE
13 }atm_error_t;
14 
15 /** 賬戶操作結構體 */
16 typedef struct {
17     char            name[20];   ///< 操作人的姓名
18     atm_Account     *account;   ///< 操作的賬戶
19     double          amt;        ///< 操作的金額
20 }atm_handler_t;
21 
22 extern atm_error_t atm_handler_main(void);
23 
24 #endif

atm_test.c

1 #include "atm_handler.h"
2 
3 int main(void)
4 {
5     atm_handler_main();
6     return 0;
7 }

Makefile

#PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
PROJECT_ROOT = $(shell pwd)
SRC_DIR = $(PROJECT_ROOT)/src
INCLUDE_DIR = $(PROJECT_ROOT)/include
OBJ_DIR = $(PROJECT_ROOT)/obj
BIN_DIR = $(PROJECT_ROOT)/bin


# 找出 src 目錄下的所有 .c 檔案
C_SRCS = $(wildcard $(SRC_DIR)/*.c)
# 將所有的 src 下的 .c 檔案替換為 .o 檔案
C_OBJS = $(patsubst %c, %o, $(C_SRCS))
TARGET = test
SHARE_LIB = libatm.a

C_SRC_MAIN = atm_test.c

CC = gcc
CCFLAGS += fPIC
LDFLAGS += -shared -fPIC
ASFLAGS +=
ARFLAGS = -crs
LIBS_FLAGS = -L$(BIN_DIR)

RM = rm -rf
 
CFLAGS += -Wall -g -I$(INCLUDE_DIR)
INCDIR += -I$(INCLUDE_DIR)



.PHONY: all clean test

all: $(TARGET)
    cp $(SHARE_LIB) $(BIN_DIR)
    cp $(SRC_DIR)/*.o $(OBJ_DIR)/
    $(RM) $(SHARE_LIB) $(SRC_DIR)/*.o

$(TARGET): $(SHARE_LIB)
    $(CC) $(C_SRC_MAIN) -o $(TARGET) $(CFLAGS) $(INCDIR) $(LIBS_FLAGS) -latm -lpthread

$(SHARE_LIB): $(C_OBJS)
    $(AR) $(ARFLAGS) $(SHARE_LIB) $(C_OBJS)
    cp $(SHARE_LIB) $(BIN_DIR)

$(C_OBJS) : %.o:%.c
    $(CC) -c $(CFLAGS) $(INCDIR) -o [email protected] $< -lpthread

clean:
    $(RM) mshell 
    $(RM) $(SHARE_LIB) 
    $(RM) $(OBJ_DIR)/$(OBJS)/*.o 
    $(RM) $(SRC_DIR)/*.o

編譯執行結果: