lock 걸고 파일 쓰기

PHP 2009. 5. 10. 18:08






flock
 


synopsis

bool flock ( resource $handle , int $operation [, int &$wouldblock ] )




description

작업할 파일에 락을 건다. fclose()가 호출되거나 스크립트가 끝나면 락은 자동으로 해제된다.




parameters

$handle: 열려진 파일의 포인터
$operation: 락의 타입을 지정
    LOCK_SH: shared lock (reader)
    LOCK_EX: exclusive lock (writer)
    LOCK_UN: lock 해제
    LOCK_NB: non-blocking
$wouldblock: lock이 block 되면 TRUE로 assign 됨. 값은 EWOULDBLOCK errno.




return

lock이 성공하면 TRUE, 실패하면 false




example

<?php

$fp 
fopen("/tmp/lock.txt""w+"
);

if (
flock($fpLOCK_EX)) { 
// do an exclusive lock
    
fwrite($fp"Write something here\n"
);
    
flock($fpLOCK_UN); 
// release the lock
} else {
    echo 
"Couldn't lock the file !"
;
}

fclose($fp
);

?>








http://php.net/flock 에는 그 외에도 재미있는 방식들이 많다.



non-blocking 모드로 하는 방법
<?php
$file = fopen('file.txt', 'w');

if(flock($file, LOCK_EX | LOCK_NB)){
    echo 'Got lock, continue writing to file';
    // Code to write to file
}else{
    echo 'File is locked by another process, aborting writing';
    // Couldn't obtain the lock immediately
}
?>






디렉토리로 lock을 구현하는 방법
<?php

define("EXCLUSIVE_LOCK", 0);
define("SHARED_LOCK", 1);

define("LOCK", 0);
define("UNLOCK", 2);

define("ADD", 1);
define("SUBSTRACT", -1);

function createLock($fileName) {
    $lockDir = "$fileName~";

    return @mkdir($lockDir);
} // createLock

function removeLock($fileName) {
    $lockDir = "$fileName~";

    if(is_dir($lockDir)) {
        rmdir($lockDir);
    } // if

    return true;
} // removeLock

function updateLockFile($fileName, $operation) {
    $lockDir = "$fileName~";
    $sharedLockFile = "$lockDir/shared";
    $exclusiveLockDir = "$lockDir/exclusive";

    while(!file_exists($sharedLockFile)) {
        if(is_dir($exclusiveLockDir)) {
            return false;
        }
        usleep(10000);
    } // while

    do {
        $locked = !(createLock($sharedLockFile));
        if($locked) {
            usleep(10000);
        } // if
    } while($locked);

    $handle = fopen($sharedLockFile, "r+b");

    $lockNumber = ord(fgetc($handle));
    if($lockNumber == 1 && $operation == SUBSTRACT) {
        fclose($handle);
        unlink($sharedLockFile);
        removeLock($sharedLockFile);
        rmdir($lockDir);
        return;
    }

    fseek($handle, 0);
    fwrite($handle, chr($lockNumber + $operation));
    fclose($handle);
    removeLock($sharedLockFile);
    return true;
} // updateLockFile

function lockFile($fileName, $operation) {
    $lockDir = "$fileName~";
    $exclusiveLockDir = "$lockDir/exclusive";
    $sharedLockFile = "$lockDir/shared";
   
    if($operation == UNLOCK) {
        if(is_dir($lockDir)) {
            if(is_dir($exclusiveLockDir)) {
                rmdir($exclusiveLockDir);
                rmdir($lockDir);
            } else {
                updateLockFile($fileName, SUBSTRACT);
            } // if
        }
        return true;
    } // if

    $locked = !(@mkdir($lockDir));
    if($locked) {
        if($operation == EXCLUSIVE_LOCK) {
            return false;
        } else {
            return updateLockFile($fileName, ADD);
            return true;
        } // if
    } else {
        if($operation == EXCLUSIVE_LOCK) {
            mkdir($exclusiveLockDir);
        } else {
            createLock($sharedLockFile);
            touch($sharedLockFile);
            $handle = fopen($sharedLockFile, "wb");
            fwrite($handle, chr(1));
            fclose($handle);
            removeLock($sharedLockFile);
        } // if
        return true;
    } // if
} // lockFile

echo lockFile("a.txt", UNLOCK) ? "gelukt" : "mislukt";
?> 





fopen($fp, "w+")로 하면 일단 파일을 다 날리고 시작하기 때문에 락을 거는데 실패하면 엿되므로
fopen($fp, "a") 로 하고 락이 성공하면 그때 ftruncate($fp, 0) 으로 날리고 시작하라는 조언도 있고...

<?php
$fp = fopen('yourfile.txt', 'a') ; 

if (flock($fp, LOCK_EX)) {
    ftruncate($fp, 0) ; // <-- this will erase the contents such as 'w+'
   
    fputs($fp, 'test string') ;
   
    flock($fp, LOCK_UN) ;


fclose($fp) ;
?> 








파일에 락요청이 초당 20개 정도 들어올 경우 CPU 점유율 100%를 먹으면서 뻗어버리는 경우가 있기 때문에,
그에 대한 해법.

<?php
// waiting until file will be locked for writing (1000 milliseconds as timeout)
if ($fp = fopen($fileName, 'a')) {
  $startTime = microtime();
  do {
    $canWrite = flock($fp, LOCK_EX);
    // If lock not obtained sleep for 0 - 100 milliseconds, to avoid collision and CPU load
    if(!$canWrite) usleep(round(rand(0, 100)*1000));
  } while ((!$canWrite)and((microtime()-$startTime) < 1000)); 

  //file was locked so now we can store information
  if ($canWrite) {
    fwrite($fp, $dataToSave);
  }
  fclose($fp);                                 
}?>




그 외에도 밑으로 쭉쭉쭉....




'PHP' 카테고리의 다른 글

PHP Application을 가장 빠르게  (0) 2009.05.15
URLEncode Code Chart  (0) 2009.05.15
정규표현식  (0) 2009.05.06
php.ini disable_functions  (0) 2009.05.06
인코딩 체크  (0) 2009.05.03
Posted by bloodguy
,