對底層代碼進行 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
嘛, 重上面的分析知道,產生死鎖的條件有這些:(窩的見解, 理論的說法請自行百度)程序員
存在兩個以上的鎖(這是理所固然的啦, 若是隻有一個鎖,腫麼可能存在死鎖問題QAQ, 因此若是多線程共用一個鎖,那就大膽的使用, 不用擔憂死鎖啦)
線程獲取鎖的順序不一致, 理論一點的說法就是資源存在環形鏈(能夠這麼說,若是有多個鎖, 可是全部的得到鎖的順序是同樣的,那麼這些鎖其實等價於一個鎖, 多塊內存也能夠看作一個大內存啦, 這個能夠用反證法證實)
個人處理方法是這樣的(各位大佬有更好的想法求評論區@)
減小鎖嵌套, 也就是臨界區的代碼量儘可能小, 儘可能代碼分開加鎖,好比一個方法可能頭部幾行代碼須要加鎖, 和結束的幾行須要加同一個鎖, 咱們就不要將整個方法加鎖了, 而是開頭加鎖,在結尾也加鎖, 由於其餘代碼也有可能加鎖了,若是方法整個加鎖,辣麼鎖嵌套就沒辦法避免了
不要有循環鎖的存在, 好比保持全部鎖的獲取順序是相同的,辣麼就不會有環路等待的條件啦(我在jni代碼裏面就是這麼處理的)api
嘛,互斥鎖的主要做用是爲了讓代碼進入臨界區, 讓一段代碼能夠成爲原子操做, 理論上將這個功能只能由操做系統提供, 畢竟操做系統負責任務調度, 管理中斷信息, 在 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);