NDK 線程同步

使用場景

對底層代碼進行 HOOK, 不可避免的要考慮多線程同步問題, 固然也能夠寫個相似 java 的線程本地變量來隔離內存空間。java

死鎖分析

恩, 道理其實你們都懂的, 畢竟大學就學了操做系統,理論神馬的窩就不講了哈, 這裏說說個人處理方法。首先線程同步問題主要是多線程對相同的可寫內存進行操做致使的, 辣麼咱們給這些可寫的內存,每一個內存都加把鎖不就得了, 哈哈,就這麼簡單, 咱們將存在多個線程訪問的一塊內存(好比說一個變量, 一個結構體,一個類)都配把鎖, 這樣確實就解決線程同步問題了, 可是做爲一隻程序員,必須打起12分的精神,時刻當心本身是否會掉坑裏面去,這裏也是哦, 若是按上面這麼作,確實解決了多線程數據不一致問題,可是極有可能致使其餘問題的出現, 好比死鎖, 性能低下等, 固然咱也不須要慌, 上面的設計思路是正確的(可寫的多線程共享內存加鎖),咱們能夠添加一些規範來規避這些問題;要想解決一個問題, 咱們必須可以知道這個問題發生的緣由是什麼linux

死鎖的簡介:
線程A1, A2, 資源 R1, R2, 對應鎖 RL1, RL2
A1的某個操做須要RL1, RL2
A2的某個操做須要RL2, RL1
說明: A1在獲取RL2鎖的時候阻塞了, A2在獲取RL1鎖的時候阻塞了c++

RL1            RL2
A 1--------------R1-------x------R2
                                    
                                    
RL2              RL1
A2  --------------R2-------x-------R1

嘛, 重上面的分析知道,產生死鎖的條件有這些:(窩的見解, 理論的說法請自行百度)程序員

  1. 存在兩個以上的鎖(這是理所固然的啦, 若是隻有一個鎖,腫麼可能存在死鎖問題QAQ, 因此若是多線程共用一個鎖,那就大膽的使用, 不用擔憂死鎖啦)
  2. 線程獲取鎖的順序不一致, 理論一點的說法就是資源存在環形鏈(能夠這麼說,若是有多個鎖, 可是全部的得到鎖的順序是同樣的,那麼這些鎖其實等價於一個鎖, 多塊內存也能夠看作一個大內存啦, 這個能夠用反證法證實)

如何解決死鎖

個人處理方法是這樣的(各位大佬有更好的想法求評論區@)
減小鎖嵌套, 也就是臨界區的代碼量儘可能小, 儘可能代碼分開加鎖,好比一個方法可能頭部幾行代碼須要加鎖, 和結束的幾行須要加同一個鎖, 咱們就不要將整個方法加鎖了, 而是開頭加鎖,在結尾也加鎖, 由於其餘代碼也有可能加鎖了,若是方法整個加鎖,辣麼鎖嵌套就沒辦法避免了
不要有循環鎖的存在, 好比保持全部鎖的獲取順序是相同的,辣麼就不會有環路等待的條件啦(我在jni代碼裏面就是這麼處理的)api

linux 互斥鎖

嘛,互斥鎖的主要做用是爲了讓代碼進入臨界區, 讓一段代碼能夠成爲原子操做, 理論上將這個功能只能由操做系統提供, 畢竟操做系統負責任務調度, 管理中斷信息, 在 Java 和c++11 中都直接提供了api,c++11 中是 std::mutex, 然而個人 ndk 環境並無這個 api,因此就用了 linux 系統裏面的 api 來實現了。數據結構

互斥鎖

(互斥鎖 = synchronized)
API說明多線程

//數據結構:
                    pthread_mutex_t
 //    行爲:
 //       初始化:
 //          靜態初始化:
                        pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
                                        
 //          動態初始化:
                        int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
       
 //          得到鎖:
                        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);
  
 // 以上能夠發現命名規律, 文件名前綴_抽象的工能名_具體事務
 // 嘛,寫c程序的話能夠這麼命名, 用c++的就沒必要啦

使用說明

初始化----加鎖--------釋放鎖------銷燬鎖函數

下面是我在ndk中封裝的Lock性能

//SimpleLock.h;
   class SimpleLock
   {
       public :
       SimpleLock();
       void lock();
       void unlock();
       ~SimpleLockl();
 
       private:
       pthread_mutex_t __m;
     }
                      

  //SimpleLock.cpp
   SimpleLock::SimpleLock()
   {
      __m = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
   }

   void SimpleLock::lock()
    {
       int ret = pthread_mutex_lock(&__m);
       assert(ret == 0);
   }

   void SimpleLock::unlock()
   {
       int ret = pthread_mutex_unlock(&__m);
       assert(ret == 0);
   }

   SimpleLock::~SimpleLock()
   {
       pthread_mutex_destory(&__m);
   }

特別注意

這裏的加鎖和解鎖應該配對,應該配對,應該配對(重要的事情要說三變) ,最後不用了應該銷燬, 應爲這部份內存是沒辦法回收的, 因此說不用的時候要銷燬掉,嘛,這裏就寫到析構函數裏面啦, 若是是 c 寫的...(額, 別忘了就好)操作系統

bug: 探針的獲取ndk數據代碼裏面也是加了鎖的,然而正常狀況下沒啥問題,若是數據有錯, 該方法會直接返回, 可是返回的時候沒有解鎖!!!最後第二次獲取數據的時候程序無響應了!!!

條件鎖

(原本只想將總結互斥鎖,可是條件鎖很好玩,也就順帶看一下啦)

//嘛, 條件鎖的做用我理解的就是linux給咱們實現的一個簡單的觀察者。
//      主要結構:
//      數據結構:
                                 pthread_cond_t cond_lock;
//      初始化:
//      靜態:
                                 PTHREAD_COND_INITIALIZER
 
//      動態: 
                                 int pthread_cond_init(pthread_cond_t *cv,const pthread_condattr_t *cattr)
//      行爲:
//      等待:
                                 int pthread_cond_wait(pthread_cond_t *cv,pthread_mutex_t *mutex);
//      通知:  
                                 int pthread_cond_signal(pthread_cond_t *cv);

使用場景

在臨界區中某個由於知足某個條件而阻塞, 又應爲某個條件知足而被喚醒,主要是若是不用條件鎖, 若是某個線程須要特定條件才能執行, 而這個條件依賴去其餘線程, 那麼就要讓這個線程隔一段時間查詢一下狀態,知足則執 行,不然阻塞,這樣會致使性能問題, 因此條件鎖能夠在狀態知足條件的時候通知阻塞的線程執行。並且通常來講條件鎖和互斥鎖要一塊兒用。

pthread_mutex_t m;
     pthread_cond_t c;


  線程一:
       pthread_mutex_lock(&m);
        if (條件不知足)
            {
                  pthread_cond_wait(&c, &m);//等待狀態知足後再執行
            }
         pthread_mutex_unlock(&m);
                     
  線程二:
        pthread_mutex_lock(&m);
               ...
        if (條件知足) //線程一能夠執行了
           {
                  pthread_cond_signal(&c);
           }
               ...
        pthread_mutex_unlock(&m);
相關文章
相關標籤/搜索