18

APC で変数を更新しようとしていますが、多くのプロセスがそれを実行しようとしています。

APC はロック機能を提供していないため、他のメカニズムを使用することを検討しています...これまでに見つかったのは、mysql の GET_LOCK() と php の flock() です。他に検討する価値はありますか?

更新: sem_acquire を見つけましたが、ブロッキング ロックのようです。

4

11 に答える 11

17
/*
CLASS ExclusiveLock
Description
==================================================================
This is a pseudo implementation of mutex since php does not have
any thread synchronization objects
This class uses flock() as a base to provide locking functionality.
Lock will be released in following cases
1 - user calls unlock
2 - when this lock object gets deleted
3 - when request or script ends
==================================================================
Usage:

//get the lock
$lock = new ExclusiveLock( "mylock" );

//lock
if( $lock->lock( ) == FALSE )
    error("Locking failed");
//--
//Do your work here
//--

//unlock
$lock->unlock();
===================================================================
*/
class ExclusiveLock
{
    protected $key   = null;  //user given value
    protected $file  = null;  //resource to lock
    protected $own   = FALSE; //have we locked resource

    function __construct( $key ) 
    {
        $this->key = $key;
        //create a new resource or get exisitng with same key
        $this->file = fopen("$key.lockfile", 'w+');
    }


    function __destruct() 
    {
        if( $this->own == TRUE )
            $this->unlock( );
    }


    function lock( ) 
    {
        if( !flock($this->file, LOCK_EX | LOCK_NB)) 
        { //failed
            $key = $this->key;
            error_log("ExclusiveLock::acquire_lock FAILED to acquire lock [$key]");
            return FALSE;
        }
        ftruncate($this->file, 0); // truncate file
        //write something to just help debugging
        fwrite( $this->file, "Locked\n");
        fflush( $this->file );

        $this->own = TRUE;
        return TRUE; // success
    }


    function unlock( ) 
    {
        $key = $this->key;
        if( $this->own == TRUE ) 
        {
            if( !flock($this->file, LOCK_UN) )
            { //failed
                error_log("ExclusiveLock::lock FAILED to release lock [$key]");
                return FALSE;
            }
            ftruncate($this->file, 0); // truncate file
            //write something to just help debugging
            fwrite( $this->file, "Unlocked\n");
            fflush( $this->file );
            $this->own = FALSE;
        }
        else
        {
            error_log("ExclusiveLock::unlock called on [$key] but its not acquired by caller");
        }
        return TRUE; // success
    }
};
于 2010-10-13T10:37:50.527 に答える
10

ファイル システムや mysql に頼ることなく 、 apc_add関数を使用してこれを実現できます。apc_add変数がまだ保存されていない場合にのみ成功します。したがって、ロックのメカニズムを提供します。TTL を使用して、失敗したロック所有者がロックを永久に保持し続けないようにすることができます。

その理由apc_addは、正しい解決策は、ロックのチェックと「ロック済み」への設定の間に存在する競合状態を回避するためです。まだ設定されてapc_addいない場合にのみ値を設定する (キャッシュに「追加」する) ため、時間の近さに関係なく、一度に 2 つの呼び出しでロックを取得できないことが保証されます。ロックのチェックと設定を同時に行わないソリューションは、本質的にこの競合状態に悩まされることはありません。競合状態なしで正常にロックするには、1 つのアトミック操作が必要です。

APC ロックはその php 実行のコンテキストでのみ存在するため、ホスト間のロックをサポートしていないため、一般的なロックにはおそらく最適なソリューションではありません。 Memcacheもアトミック追加機能を提供するため、ホスト間でロックする方法の 1 つであるこの手法で使用することもできます。 Redisまた、アトミックな「SETNX」機能と TTL もサポートしており、ホスト間のロックと同期の非常に一般的な方法です。ただし、OPは特にAPCのソリューションを要求しています。

于 2012-10-07T03:57:52.493 に答える
5

ロックの目的が、複数のプロセスが空のキャッシュ キーに値を設定しようとするのを防ぐことである場合、ブロッキング ロックを使用しない理由はありません。


  $value = apc_fetch($KEY);

  if ($value === FALSE) {
      shm_acquire($SEMAPHORE);

      $recheck_value = apc_fetch($KEY);
      if ($recheck_value !== FALSE) {
        $new_value = expensive_operation();
        apc_store($KEY, $new_value);
        $value = $new_value;
      } else {
        $value = $recheck_value;
      }

      shm_release($SEMAPHORE);
   }

キャッシュが良好な場合は、そのまま使用します。キャッシュに何もない場合は、ロックを取得します。ロックを取得したら、キャッシュをダブルチェックして、ロックを取得するのを待っている間にキャッシュが再作成されていないことを確認する必要があります。キャッシュが再作成された場合は、その値を使用してロックを解放します。それ以外の場合は、計算を行い、キャッシュを作成してからロックを解放します。

于 2008-12-02T19:53:50.173 に答える
3

実際に、これが Peter の提案よりもうまく機能するかどうかを確認してください。

http://us2.php.net/flock

排他ロックを使用し、それに慣れている場合は、ファイルをロックしようとした他のすべてを 2 ~ 3 秒のスリープ状態にします。正しく行われた場合、サイトはロックされたリソースに関するハングを経験しますが、同じものをキャッシュするために戦うスクリプトの大群は発生しません.

于 2008-12-02T19:26:35.160 に答える
3

ロックをファイルシステムに基づいて行うことを気にしない場合は、モード 'x' で fopen() を使用できます。次に例を示します。

$f = fopen("lockFile.txt", 'x');
if($f) {
    $me = getmypid();
    $now = date('Y-m-d H:i:s');
    fwrite($f, "Locked by $me at $now\n");
    fclose($f);
    doStuffInLock();
    unlink("lockFile.txt"); // unlock        
}
else {
    echo "File is locked: " . file_get_contents("lockFile.txt");
    exit;
}

www.php.net/fopen を参照

于 2008-11-30T23:22:56.540 に答える
1

これは1年前のものだと思いますが、PHPのロックについて自分で調査しているときに、この質問に出くわしました.

APC自体を使用して解決策が可能かもしれないと思います。私を狂ったと呼んでください、しかしこれは実行可能なアプローチかもしれません:

function acquire_lock($key, $expire=60) {
    if (is_locked($key)) {
        return null;
    }
    return apc_store($key, true, $expire);
}

function release_lock($key) {
    if (!is_locked($key)) {
        return null;
    }
    return apc_delete($key);
}

function is_locked($key) {
    return apc_fetch($key);
}

// example use
if (acquire_lock("foo")) {
    do_something_that_requires_a_lock();
    release_lock("foo");
}

実際には、既存の APC キーとの衝突を防ぐためだけに、ここで使用するキーを生成する別の関数をそこにスローすることがあります。

function key_for_lock($str) {
    return md5($str."locked");
}

この$expireパラメータは、スクリプトが終了した場合などにロックが永久に保持されるのを防ぐため、APC の使用に適した機能です。

この回答が、1年後にここでつまずいた人にとって役立つことを願っています。

于 2010-01-29T22:06:49.960 に答える
0

これが仕事を処理する最良の方法であるかどうかはわかりませんが、少なくとも便利です。

function WhileLocked($pathname, callable $function, $proj = ' ')
{
    // create a semaphore for a given pathname and optional project id
    $semaphore = sem_get(ftok($pathname, $proj)); // see ftok for details
    sem_acquire($semaphore);
    try {
        // capture result
        $result = call_user_func($function);
    } catch (Exception $e) {
        // release lock and pass on all errors
        sem_release($semaphore);
        throw $e;
    }

    // also release lock if all is good
    sem_release($semaphore);
    return $result;
}

使い方はこのように簡単です。

$result = WhileLocked(__FILE__, function () use ($that) {
    $this->doSomethingNonsimultaneously($that->getFoo());
});

3 番目のオプション引数は、この関数をファイルごとに複数回使用する場合に便利です。

最後に大事なことを言い忘れましたが、この関数を (署名を保持したまま) 後で別の種類のロック メカニズムを使用するように変更することは難しくありません。たとえば、複数のサーバーを使用している場合などです。

于 2016-04-27T08:14:16.283 に答える
0

EAcceleratorにはそのためのメソッドがあります。eaccelerator_lockeaccelerator_unlock

于 2011-05-02T11:14:03.340 に答える
0

APC は現在、保守されておらず、死んでいると見なされています。後継のAPCuは を介し​​てロックを提供しますapcu_entry。ただし、他の APCu 関数の同時実行も禁止されることに注意してください。ユースケースによっては、これで問題ない場合があります。

マニュアルから:

注:制御apcu_entry()がキャッシュのロックに入ると排他的に取得され、制御が終了すると解放されapcu_entry()ます。実際には、これにより本体がgeneratorクリティカル セクションになり、2 つのプロセスが同じコード パスを同時に実行できなくなります。さらに、同じロックを取得するため、他の APCu 関数の同時実行を禁止します。

于 2017-01-05T09:18:13.067 に答える
-1

実際、私が見つけたのは、ロックはまったく必要ないということです...私が作成しようとしているのは、オートロードのすべてのクラス=>パスの関連付けのマップであるため、 1 つのプロセスが他のプロセスが見つけたものを上書きします (適切にコーディングされていれば、その可能性はほとんどありません)。したがって、解決策は「ロックなし」であることが判明しました。

于 2008-12-03T17:29:49.653 に答える