天天看點

Linux線程同步與程序通信

線程同步與程序通信

線程同步

互斥量

  • 臨界資源:在一段時間内隻允許一個任務(線程或程序)通路資源。任務之間采用互斥的方式通路臨界資源
  • 互斥量:

    pthread_mutex_t mutex;

    初始化、加鎖、解鎖、銷毀
    • 加鎖:

      int pthread_mutex_lock(); int pthread_mutex_trylock();

      在通路臨界資源前,對互斥量進行加鎖。

      trylock()

      未加鎖則加鎖,加鎖後不重複加鎖。
    • 解鎖:

      int pthread_mutex_unlock();

      通路完成後對互斥量解鎖
    • 銷毀:

      int pthread_mutex_destory();

      互斥量使用完畢後對互斥量進行銷毀,釋放資源。

條件變量

  • 任務同步:多個線程都要通路臨界資源又要互相合作,一個任務的執行依賴與另一個任務的執行情況。
  • 條件變量:

    pthread_cond_t cond;

    和互斥量一起使用,允許線程以互斥的方式阻塞等待條件的發生即為同步關系
    • 觸發條件變量的線程:互斥量加鎖>>某一操作>>觸發條件變量>>互斥量解鎖
    • 等待條件變量的線程:互斥量加鎖>>等待條件變量>>某一操作>>互斥量解鎖
    • 銷毀:

      pthread_cond_destory();

      條件變量不再使用後進行銷毀,釋放空間
    • 等待條件變量:

      pthread_cond_wait();

      使得調用程序進入阻塞狀态,直到條件觸發
    • 觸發條件變量:

      pthread_cond_signal();/pthread_cond_broadcast();

      可觸發并喚醒等待條件變量的線程
  • 條件變量與互斥量要配合使用
    • 條件變量的使用場景伴随着臨界資源的使用
    • 在調用

      pthread_cond_wait();

      前需要使互斥量處于加鎖狀态,通過原子操作将調用線程放到該條件變量等待線程隊列(臨界資源)中
      //等待條件變量的操作
      pthread_mutex_lock();
      pthread_cond_wait();
      pthread_mutex_unlock();
                 
  • 條件變量的使用

讀寫鎖

  • 讀寫關系:讀寫互斥、寫寫互斥、讀讀不互斥
  • 讀寫鎖:

    pthread_rwlock_t;

    • 加讀鎖:

      阻塞加鎖pthread_rwlock_rdlock();/非阻塞加鎖pthread_rwlock_tryrdlock();/限時加讀鎖pthread_rwlock_timedrdlock();

    • 加寫鎖:

      阻塞加鎖pthread_rwlock_wrlock();/非阻塞加鎖pthread_rwlock_trywrlock();/限時加讀鎖pthread_rwlock_timedwrlock();

    • 解鎖:統一解鎖函數,最近配對原則解鎖

      pthread_rwlock_unlock();

程序通信

  • 管道和命名管道:檔案的形式實作不同程序共享資源
  • XSI IPC機制:LINUX系統使用的程序通信機制
    功能 信号量 共享記憶體 消息隊列
    建立或打開一個IPC對象,獲得對IPC機制的通路權 semget shmget msgget
    IPC操作:信号量操作;連接配接/釋放共享記憶體;發送/接收消息 semop shmat/shmdt msgsnd/msgrcv
    IPC控制:獲得/修改IPC對象狀态,“删除”IPC對象 semctl shmctl msgctl

消息隊列

  • 消息隊列:消息的連結清單,有寫或者讀權限的程序可以按照一定的規則添加或者讀取消息
  • 消息隊列的操作

    sys/types.h sys/ipc.h sys/msg.h

    • 打開或建立消息隊列對象:

      int msgget();

    • 從消息隊列接收消息:

      int msgrcv();

    • 向消息隊列發送消息:

      int msgsnd();

    • 消息隊列控制操作:

      int msgctl();

信号量

  • 互斥信号量:任務之間互斥通路臨界資源 初始值為1
  • 計數信号量:任務之間競争通路共享資源 初始值大于1,表示共享資源個數
  • 二值信号量:任務之間的同步機制 初始值為0
  • 信号量的資料結構
    struct s
    {
        int count; //資源計數
        Queue queue; //任務阻塞隊列
    };
    
    int init(); // 初始化
    int P(); // P操作
    int V(); // V操作
               
  • P操作:配置設定資源
    --s.count;
    if(s.count < 0)
    {
    	調用程序進入s.queue;
    	阻塞調用程序;
    }
               
  • V操作:釋放資源
    ++s.count;
    if(s.count <= 0 )
    {
    	從阻塞隊列s.queue中取出一個程序P;
    	程序P進入就緒隊列;
    }
               
  • 信号量集的操作

    sys/types.h sys/ipc.h sys/sem.h

    • 建立或者打開信号量集對象

      int semget();

      #include<stdio.h>
      #include<sys/sem.h>
      #define MYKEY 0x1a0a //定一個ipc的key值
      int main()
      {
      	int semid;
      	semid = semget(MYKEY,1,IPC_CREAT|IPC_R|IPC_W|IPC_M); //建立一個信号量集對象
      	printf("semid = %d\n",semid);
          return 0;
      }
                 
    • 信号量PV操作

      int semop();

      :根據參數

      short sem_op;

      判斷執行的操作類型
      • sem_op > 0

        :信号量V操作,增加信号量的值
      • sem_op < 0

        :信号量P操作,減少信号量的值
      • sem_op = 0

        :對信号量的目前值是否為0的測試
      //實作P操作
      static int semaphore_v(void)
      {
      	struct sembuf sem_b;
      	sem_b.sem_num = 0; //信号量編号
      	sem_b.sem_op = -1; // P操作
      	sem_b.sem_fg = SEM_UNDO; //在程序沒釋放信号量而退出時,系統自動釋放該程序中未釋放的信号量
      	if(semop(sem_id, &sem_b,1) == -1)
      	{
      		fprintf(stderr,"semaphore_p failed\n");
      		return 0;
      	}
      	return 1;
      }
      
      //實作V操作
      static int semaphore_v(void)
      {
      	struct sembuf sem_b;
      	sem_b.sem_num = 0;
      	sem_b.sem_op = 1; // V操作
      	sem_b.sem_fg = SEM_UNDO;
      	if(semop(sem_id, &sem_b,1) == -1)
      	{
      		fprintf(stderr,"semaphore_v failed\n");
      		return 0;
      	}
      	return 1;
      }
                 
    • 信号量控制

      int semctl();

      int cmd;

      控制指令參數
      • 對信号量集的控制指令:

        IPC_RMID

        删除;

        IPC_SET

        設定參數;

        IPC_STAT

        擷取參數;

        IPC_INFO

        擷取系統資訊
      • 對信号量的控制指令:

        SETVAL

        設定信号量的值;

        GETVAL

        擷取信号量的值;

        GETPID

        擷取信号量擁有者的PID值
      //信号量初始化
      int init_sem(int sem_id, int init_value)
      {
      	union semun_sem_union;
      	sem_union.val = init_value;
      	if(semctl(sem_id,0,SETVAL,sem_union) == -1)
      	{
      		perror("Initialize semaphore");
      		return -1;
      	}
      	return 0;
      }
      
      //從系統中删除信号量
      int del_sem(int sem_id)
      {
      	union semun sem_union;
      	if(semctl(sem_id,0,IPC_RMID,sem_union) == -1)
      	{
      		perror("Initialize semaphore");
      		return -1;
      	}
      }
                 

共享記憶體

  • 共享記憶體:不同程序可以将同一段記憶體空間連結到自己的位址空間中,類似實體空間中的全局變量
  • 共享記憶體的操作

    sys/types.h sys/ipc.h sys/shm.h

    • 建立或者打開共享記憶體對象

      int shmget();

    • 将共享記憶體連結到調用的程序的位址空間

      void *shmat();

    • 删除程序的共享記憶體連結

      int shmdt();

    • 對共享記憶體的操作

      int shmctl();

  • 共享記憶體應用–生産者/消費者問題
    //緩存池結構
    struct BufferPool
    {
    	char Buffer[5][100];
    	int index[5];
    };
               

繼續閱讀