PHP में लॉक प्राप्त करने का सर्वोत्तम तरीका




locking apc (7)

मैं एपीसी में एक वैरिएबल को अपडेट करने की कोशिश कर रहा हूं, और ऐसा करने की कोशिश में कई प्रक्रियाएं होगी।

एपीसी लॉकिंग की कार्यक्षमता प्रदान नहीं करता है, इसलिए मैं अन्य तंत्रों का उपयोग करने पर विचार कर रहा हूं ... जो कि मैंने अभी तक पाया है MySQL का GET_LOCK (), और php के झुंड ()। पर विचार करने के लायक कुछ भी?

अद्यतन: मैंने sem_acquire पाया है, लेकिन ऐसा लगता है कि अवरुद्ध लॉक हो।


अगर आप फाइल सिस्टम पर अपने लॉक के आधार पर ध्यान नहीं देते, तो आप 'एक्स' मोड के साथ एफओपीएन () का उपयोग कर सकते हैं यहाँ एक उदाहरण है:

$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: " . get_file_contents("lockFile.txt");
    exit;
}

Www.php.net/fopen देखें


अगर लॉक का मुद्दा एक रिक्त कैश कुंजी को पॉप्युलेट करने की कोशिश करने से कई प्रक्रियाओं को रोकने के लिए है, तो आप अवरुद्ध लॉक क्यों नहीं करना चाहते हैं?


  $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);
   }

यदि कैश अच्छा है, तो आप इसके साथ ही रोल करें अगर कैश में कुछ नहीं है, तो आपको लॉक मिलता है आपके पास लॉक होने के बाद, यह सुनिश्चित करने के लिए आपको कैश को दोबारा जांचना होगा, जब आप लॉक प्राप्त करने के लिए प्रतीक्षा कर रहे थे, कैश को फिर से नहीं किया गया था। यदि कैश को पुन: स्थापित किया गया था, तो उस मान का उपयोग करें और ताला जारी करें, अन्यथा, आप गणना करते हैं, कैश को पॉप्यूलेट करें और फिर अपना लॉक जारी करें


मैंने जो पाया है, वास्तव में, यह है कि मुझे किसी भी लॉकिंग की ज़रूरत नहीं है ... मैं जो कुछ भी बनाने की कोशिश कर रहा हूं वह सभी वर्गों का एक नक्शा है, जो कि autoload के लिए पथ संघों का है, इसका कोई फर्क नहीं पड़ता एक प्रक्रिया दूसरे को मिलती है, पर अधिलेखित करता है (यह ठीक से कोडित नहीं है, यह अत्यधिक संभावना नहीं है), क्योंकि डेटा अंततः वैसे भी मिलेगा। इसलिए, समाधान "कोई ताला नहीं" हो गया


मुझे पता है यह एक वर्ष का है, लेकिन मैं PHP पर लॉकिंग पर कुछ शोध कर रहा हूं, लेकिन मैं इस सवाल पर केवल ठोकर खाई।

यह मेरे लिए होता है कि एक एपीसी का उपयोग करके एक समाधान संभव हो सकता है मुझे पागल कहते हैं, लेकिन यह एक व्यावहारिक दृष्टिकोण हो सकता है:

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");
}

अभ्यास में मैं किसी अन्य एप को फेंक सकता हूं जो यहां उपयोग करने के लिए एक कुंजी उत्पन्न करता है, बस एक मौजूदा एपीसी कुंजी के साथ टकराव को रोकने के लिए, जैसे:

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

$expire पैरामीटर उपयोग करने के लिए एपीसी की एक अच्छी सुविधा है, क्योंकि यह आपके लॉक को हमेशा के लिए आयोजित होने से रोकता है यदि आपकी स्क्रिप्ट मर जाती है या ऐसा कुछ।

उम्मीद है कि यह उत्तर किसी और के लिए सहायक होगा जो एक साल बाद यहां ठोकर खाएगा।


आप सिस्टम फाइल या आईएसएसएल के सहारे बिना इसे हासिल करने के लिए एपीसी_एड फंक्शन का उपयोग कर सकते हैं। apc_add केवल तब सफल होता है जब चर पहले से संग्रहीत नहीं होता; इस प्रकार, लॉकिंग की व्यवस्था प्रदान करना टीटीएल का उपयोग यह सुनिश्चित करने के लिए किया जा सकता है कि फॉइल किए गए लॉकहार्ड लॉक को हमेशा के लिए नहीं रखेंगे।

कारण apc_add सही समाधान है क्योंकि यह रेस की स्थिति से बचा जाता है जो अन्यथा लॉक की जांच करने और इसे 'आपके द्वारा लॉक' करने के लिए स्थापित करने के बीच मौजूद होगा। चूंकि apc_add केवल मान निर्धारित करता है अगर यह पहले से सेट नहीं है (कैश में "इसे जोड़ता है"), तो यह सुनिश्चित करता है कि समय पर उनकी निकटता के बावजूद ताला को एक ही बार में दो कॉल से प्राप्त नहीं किया जा सकता है। एक ही समय में लॉक की जाँच और सेट नहीं करने वाला कोई भी समाधान इस दौड़ की स्थिति से स्वाभाविक रूप से पीड़ित होगा; एक परमाणु आपरेशन को दौड़ की स्थिति के बिना सफलतापूर्वक लॉक करना आवश्यक है।

चूंकि एपीसी लॉक केवल उस पीएचपी निष्पादन के संदर्भ में मौजूद होंगे, सामान्य लॉकिंग के लिए शायद यह सबसे अच्छा समाधान नहीं है, क्योंकि यह मेजबान के बीच ताले का समर्थन नहीं करता है। Memcache भी एक परमाणु जोड़ समारोह प्रदान करता है और इस प्रकार इस तकनीक के साथ भी इस्तेमाल किया जा सकता है - जो मेजबानों के बीच लॉकिंग का एक तरीका है। रेडिस परमाणु 'एसईटीएनएक्स' फ़ंक्शन और टीटीएल का भी समर्थन करता है, और मेजबानों के बीच लॉकिंग और सिंक्रनाइज़ेशन का एक बहुत ही सामान्य तरीका है। हाउवर, ओपी विशेष रूप से एपीसी के लिए एक समाधान का अनुरोध करता है।


यह नहीं कह सकता कि नौकरी संभालने का यह सबसे अच्छा तरीका है, लेकिन कम से कम यह सुविधाजनक है

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());
});

तीसरा वैकल्पिक तर्क यदि आप इस फ़ंक्शन का उपयोग प्रति फ़ाइल एक से अधिक बार करते हैं, तो आसान हो सकता है।

आखिरी लेकिन कम से कम यह किसी अन्य प्रकार की लॉकिंग तंत्र को बाद की तारीख में उपयोग करने के लिए इस फ़ंक्शन को संशोधित करना मुश्किल नहीं है (उदाहरण के लिए अपने हस्ताक्षर रखते हुए), उदाहरण के लिए यदि आपको कई सर्वरों के साथ काम करने का अवसर मिलता है


/*
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
    }
};




apc