php基於redis的lock-free

加速處理隊列的方式有不少種,多線程,多進程,多開任務(下文都統稱任務)等等,這些方法在併發訪問 隊列(這裏的隊列都是指redis的zset) 數據的時候,由於是共享同一個隊列,容易產生不少問題。php

在衆多解決辦法中,加鎖是最簡單粗暴的方法 (目前不考慮同時獲取鎖的清,php感受不太好處理,除非c寫擴展) 這中鎖也叫互斥鎖,就是同一時刻只有得到鎖的那個任務纔有資格去操做共享資源, 別的任務都阻塞住了,被放到了一個叫鎖池(Lock pool)的地方,什麼事情都幹不了,浪費了不少資源。redis

lock-free無鎖隊列 通常的是基於CAS(compareAndSet)實現的,可是php操做比較麻煩,得用c寫擴展bash

intcompare_and_swap (int* reg, intoldval, intnewval)  
{  
  intold_reg_val = *reg;  
  if(old_reg_val == oldval)  
     *reg = newval;  
  returnold_reg_val;  
}
複製代碼

這裏用redis的set實現,原理就是每個隊列訪問的時候,它都是隻取一個值的,取到值以後,存到set裏面,若是有返回0沒有則返回1,若是這個時候等於0,那麼說明正在有任務處理它,就去取隊列的下一個多線程

先生成一些測試數據併發

$redis = $this->DomainDrivenDesign('RedisLib');
    $key = "cas:";
    for(;;){
        $value = time()+mt_rand(0,1000000);
        $score = $value+mt_rand(0,1000000);
        $redis->zAdd($key."zset",$value,$score);
    }
複製代碼

處理數據代碼測試

$redis   = $this->DomainDrivenDesign('RedisLib');
        $keyZSet = "cas:zset";
        $keySet  = "cas:zadd";
        for (; ;) {
            ob_flush();
            $start = 0;
            $end   = 0;
            start:
            flush();
            ob_flush();
            $queueVal = $redis->zRevRange($keyZSet, $start, $end)[0];
            $val      = $redis->sAdd($keySet, $queueVal);
            if ($val == 0) {
                $start++;
                $end++;
                goto start;
            }
            for (;;){
                flush();
                ob_flush();
                echo $queueVal . "\r\n";
            }

        }
複製代碼

第二個for換成對應處理數據的代碼就好了ui

測試一下,這裏同時開了3個 this

第一個 spa

第二個
第三個
相關文章
相關標籤/搜索