PHP의 파일 잠금, 뮤텍스 잠금 및 읽기-쓰기 잠금에 대한 자세한 설명

*文
풀어 주다: 2023-03-18 19:14:01
원래의
3812명이 탐색했습니다.

이 글에서는 주로 PHP 프로그램의 파일 잠금, 뮤텍스 잠금 및 읽기-쓰기 잠금 사용 기술을 소개하며, 동기화 모듈의 사용 예를 중점적으로 다루며 필요한 친구들이 참고할 수 있습니다. 그것이 모두에게 도움이 되기를 바랍니다.

파일 잠금
전체 이름은 책에 언급된 권고 파일 잠금입니다. 이러한 유형의 잠금은 비교적 일반적입니다. 예를 들어, mysql과 php-fpm이 시작된 후에는 프로세스 ID를 기록하는 pid 파일이 있습니다.

이 잠금을 사용하면 프로세스가 반복적으로 실행되는 것을 방지할 수 있습니다. 예를 들어 crontab을 사용하면 1분마다 하나의 작업이 실행되도록 제한되지만 프로세스 잠금을 사용하여 문제를 해결하지 않으면 이 프로세스가 1분 이상 실행될 수 있습니다. 충돌이 발생하여 두 프로세스를 함께 실행하면 문제가 발생합니다.

PID 파일 잠금 사용의 또 다른 장점은 프로세스가 중지 또는 다시 시작 신호를 자체적으로 보내는 것이 편리하다는 것입니다. 예를 들어 php-fpm을 다시 시작하는 명령은

kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`
pid 파일에 기록된 프로세스에 USR2 신호를 보냅니다. 신호는 프로세스 통신에 속하며 별도로 처리됩니다.

PHP의 인터페이스는 매우 다양하며 문서는 비교적 자세합니다. 먼저 정의를 살펴보겠습니다. bool Flock (resource $handle , int $Operation [, int &$wouldblock ] ).

  • $handle은 일반적으로 fopen()에 의해 생성되는 리소스인 파일 시스템 포인터입니다. . 이는 Flock을 사용하려면 파일을 열어야 함을 의미합니다.

  • $Operation은 작업 유형입니다.

  • &$wouldblock 잠금이 차단되면 이 변수는 1로 설정됩니다.

이 기능은 기본적으로 차단된다는 점에 유의하세요. 작업에 대한 비트마스크 LOCK_NB를 내려서 테스트해 보세요.


$pid_file = "/tmp/process.pid";
$pid = posix_getpid();
$fp = fopen($pid_file, 'w+');
if(flock($fp, LOCK_EX | LOCK_NB)){
  echo "got the lock \n";
  ftruncate($fp, 0);   // truncate file
  fwrite($fp, $pid);
  fflush($fp);      // flush output before releasing the lock
  sleep(300); // long running process
  flock($fp, LOCK_UN);  // 释放锁定
} else {
  echo "Cannot get pid lock. The process is already up \n";
}
fclose($fp);
로그인 후 복사

process.php로 저장하고 php process.php &를 실행한 다음 php process.php를 다시 실행하면 오류 메시지가 표시됩니다. Flock에는 공유 잠금인 LOCK_SH도 있습니다.

뮤텍스 잠금 및 읽기-쓰기 잠금
동기화 모듈의 뮤텍스:
Mutex는 복합어, 상호 배제입니다. pecl을 사용하여 동기화 모듈을 설치하고 pecl install sync를 설치합니다. 문서의 SyncMutex에는 잠금 및 잠금 해제 두 가지 메서드만 있습니다. 바로 코드 테스트로 이동하겠습니다. 저는 IDE에서 작성하지 않았으므로 cs가 매우 추악합니다. 무시하십시오.


$mutex = new SyncMutex("UniqueName");

for($i=0; $i<2; $i++){
  $pid = pcntl_fork();
  if($pid <0){
    die("fork failed");
  }elseif ($pid>0){
    echo "parent process \n";
  }else{
    echo "child process {$i} is born. \n";
    obtainLock($mutex, $i);
  }
}

while (pcntl_waitpid(0, $status) != -1) { 
  $status = pcntl_wexitstatus($status); 
  echo "Child $status completed\n"; 
}

function obtainLock ($mutex, $i){
  echo "process {$i} is getting the mutex \n";
  $res = $mutex->lock(200);
  sleep(1);
  if (!$res){
    echo "process {$i} unable to lock mutex. \n";
  }else{
    echo "process {$i} successfully got the mutex \n";
    $mutex->unlock();
  }
  exit();
}
로그인 후 복사

mutex.php로 저장하고 php mutex.php를 실행하면 출력은


parent process 
parent process 
child process 1 is born. 
process 1 is getting the mutex 
child process 0 is born. 
process 0 is getting the mutex 
process 1 successfully got the mutex 
Child 0 completed
process 0 unable to lock mutex. 
Child 0 completed
로그인 후 복사
로그인 후 복사

여기서 하위 프로세스 0과 1이 반드시 앞에 올 필요는 없습니다. 그러나 자물쇠를 얻지 못하는 사람은 항상 있습니다. 여기서 SyncMutex::lock(int $millisecond)의 매개변수는 차단 기간을 나타내는 밀리초이고, -1은 무제한 차단을 의미합니다.

동기화 모듈의 읽기-쓰기 잠금:
SyncReaderWriter의 메서드는 유사하며, readlock, readunlock, writelock, writeunlock이 쌍으로 나타날 수 있습니다. 작성된 테스트 코드는 없습니다. 자물쇠를 교체하십시오.

동기화 모듈의 이벤트:
golang의 Cond와 더 비슷하고, wait() 블록과 fire()는 Event에 의해 차단된 프로세스를 깨웁니다. Cond를 소개하는 좋은 기사가 있습니다. Cond는 고정된 잠금 사용법임을 알 수 있습니다. SyncEvent도 마찬가지입니다.
PHP 문서의 예는 fire() 메소드가 웹 애플리케이션에서 사용되는 것으로 보입니다.

for($i=0; $i<3; $i++){
  $pid = pcntl_fork();
  if($pid <0){
    die("fork failed");
  }elseif ($pid>0){
    //echo "parent process \n";
  }else{
    echo "child process {$i} is born. \n";
    switch ($i) {
    case 0:
      wait();
      break;
    case 1:
      wait();
      break;
    case 2:
      sleep(1);
      fire();
      break;
    }
  }
}

while (pcntl_waitpid(0, $status) != -1) { 
  $status = pcntl_wexitstatus($status); 
  echo "Child $status completed\n"; 
}

function wait(){
  $event = new SyncEvent("UniqueName");
  echo "before waiting. \n";
  $event->wait();
  echo "after waiting. \n";
  exit();
}

function fire(){
  $event = new SyncEvent("UniqueName");
  $event->fire();
  exit();
}
로그인 후 복사

의 테스트 코드 여기에는 의도적으로 fire()가 하나 덜 작성되어 프로그램이 차단됩니다. 이는 fire()가 한 번에 하나의 프로세스만 깨운다는 것을 증명합니다.

pthreads 모듈
뮤텍스 잠금 및 잠금 해제:

기능:


pthread_mutex_lock (mutex) 
pthread_mutex_trylock (mutex) 
pthread_mutex_unlock (mutex)
로그인 후 복사

사용법:

뮤텍스가 다른 스레드에 의해 잠긴 경우 스레드는 pthread_mutex_lock() 함수를 사용하여 지정된 뮤텍스 변수를 잠급니다. , 이 호출은 뮤텍스가 잠금 해제될 때까지 스레드를 차단합니다.
pthread_mutex_trylock()은 뮤텍스를 잠그려고 시도합니다. 그러나 뮤텍스가 이미 잠겨 있으면 루틴은 "busy" 오류 코드와 함께 즉시 반환됩니다. 이 루틴은 pthread_mutex_trylock()에서 유용할 수 있습니다.

  Mutex. 뮤텍스가 잠겨 있으면 프로그램은 사용 중인 오류 값과 함께 즉시 반환됩니다. 이 기능은 우선순위 변경 시 교착상태를 방지하는 데 유용합니다. 스레드는 pthread_mutex_unlock()을 사용하여 자신이 차지하는 뮤텍스를 잠금 해제할 수 있습니다. 한 스레드가 보호된 데이터의 사용을 완료하고 다른 스레드가 보호된 데이터에 대해 작업하기 위해 뮤텍스를 얻으려고 할 때 이 함수를 호출할 수 있습니다. 다음 상황이 발생하면 오류가 발생합니다. 뮤텍스가 잠금 해제되었습니다. 뮤텍스가 다른 스레드에 의해 점유되었습니다. 이는 참여 스레드의 "신사 합의"입니다. 코드를 작성할 때 뮤텍스를 올바르게 잠그고 잠금 해제해야 합니다.

    Q: 동일한 잠긴 뮤텍스를 기다리는 스레드가 여러 개 있습니다. 뮤텍스가 잠금 해제되면 어떤 스레드가 뮤텍스를 가장 먼저 잠그나요?
  • A: 스레드가 우선 순위 예약 메커니즘을 사용하지 않는 한 시스템 스케줄러에 의해 스레드가 할당되며 뮤텍스를 가장 먼저 잠글 스레드는 무작위입니다.

  • #include<stdlib.h> 
    #include<stdio.h> 
    #include<unistd.h> 
    #include<pthread.h> 
    
    typedef struct ct_sum 
    { 
      int sum; 
      pthread_mutex_t lock; 
    }ct_sum; 
    
    void * add1(void *cnt) 
    {    
      pthread_mutex_lock(&(((ct_sum*)cnt)->lock)); 
      for(int i=0; i < 50; i++) 
      {
        (*(ct_sum*)cnt).sum += i;   
      } 
      pthread_mutex_unlock(&(((ct_sum*)cnt)->lock)); 
      pthread_exit(NULL); 
      return 0; 
    } 
    void * add2(void *cnt) 
    {    
      pthread_mutex_lock(&(((ct_sum*)cnt)->lock)); 
      for(int i=50; i<101; i++) 
      {  
         (*(ct_sum*)cnt).sum += i;  
      } 
      pthread_mutex_unlock(&(((ct_sum*)cnt)->lock)); 
      pthread_exit(NULL); 
      return 0; 
    } 
     
    int main(void) 
    {
      pthread_t ptid1, ptid2; 
      ct_sum cnt; 
      pthread_mutex_init(&(cnt.lock), NULL); 
      cnt.sum=0; 
     
      pthread_create(&ptid1, NULL, add1, &cnt); 
      pthread_create(&ptid2, NULL, add2, &cnt); 
      
      pthread_join(ptid1,NULL); 
      pthread_join(ptid2,NULL);
    
      printf("sum %d\n", cnt.sum);
      pthread_mutex_destroy(&(cnt.lock)); 
    
      return 0; 
    }
    로그인 후 복사

    信号量
    sync模块中的信号量:
    SyncSemaphore文档中显示,它和Mutex的不同之处,在于Semaphore一次可以被多个进程(或线程)得到,而Mutex一次只能被一个得到。所以在SyncSemaphore的构造函数中,有一个参数指定信号量可以被多少进程得到。
    public SyncSemaphore::__construct ([ string $name [, integer $initialval [, bool $autounlock ]]] ) 就是这个$initialval (initial value)


    $lock = new SyncSemaphore("UniqueName", 2);
    
    for($i=0; $i<2; $i++){
      $pid = pcntl_fork();
      if($pid <0){
        die("fork failed");
      }elseif ($pid>0){
        echo "parent process \n";
      }else{
        echo "child process {$i} is born. \n";
        obtainLock($lock, $i);
      }
    }
    
    while (pcntl_waitpid(0, $status) != -1) { 
      $status = pcntl_wexitstatus($status); 
      echo "Child $status completed\n"; 
    }
    
    function obtainLock ($lock, $i){
      echo "process {$i} is getting the lock \n";
      $res = $lock->lock(200);
      sleep(1);
      if (!$res){
        echo "process {$i} unable to lock lock. \n";
      }else{
        echo "process {$i} successfully got the lock \n";
        $lock->unlock();
      }
      exit();
    }
    로그인 후 복사

    这时候两个进程都能得到锁。

    • sysvsem模块中的信号量

    • sem_get 创建信号量

    • sem_remove 删除信号量(一般不用)

    • sem_acquire 请求得到信号量

    • sem_release 释放信号量。和 sem_acquire 成对使用。


    $key = ftok(&#39;/tmp&#39;, &#39;c&#39;);
    
    $sem = sem_get($key);
    
    for($i=0; $i<2; $i++){
      $pid = pcntl_fork();
      if($pid <0){
        die("fork failed");
      }elseif ($pid>0){
        //echo "parent process \n";
      }else{
        echo "child process {$i} is born. \n";
        obtainLock($sem, $i);
      }
    }
    
    while (pcntl_waitpid(0, $status) != -1) { 
      $status = pcntl_wexitstatus($status); 
      echo "Child $status completed\n"; 
    }
    sem_remove($sem); // finally remove the sem
    
    function obtainLock ($sem, $i){
      echo "process {$i} is getting the sem \n";
      $res = sem_acquire($sem, true);
      sleep(1);
      if (!$res){
        echo "process {$i} unable to get sem. \n";
      }else{
        echo "process {$i} successfully got the sem \n";
        sem_release($sem);
      }
      exit();
    }
    로그인 후 복사

    这里有一个问题,sem_acquire()第二个参数$nowait默认为false,阻塞。我设为了true,如果得到锁失败,那么后面的sem_release会报警告 PHP Warning: sem_release(): SysV semaphore 4 (key 0x63000081) is not currently acquired in /home/jason/sysvsem.php on line 33, 所以这里的release操作必须放在得到锁的情况下执行,前面的几个例子中没有这个问题,没得到锁执行release也不会报错。当然最好还是成对出现,确保得到锁的情况下再release。
    此外,ftok这个方法的参数有必要说明下,第一个 必须是existing, accessable的文件, 一般使用项目中的文件,第二个是单字符字符串。返回一个int。

    输出为


    parent process 
    parent process 
    child process 1 is born. 
    process 1 is getting the mutex 
    child process 0 is born. 
    process 0 is getting the mutex 
    process 1 successfully got the mutex 
    Child 0 completed
    process 0 unable to lock mutex. 
    Child 0 completed
    로그인 후 복사
    로그인 후 복사

    相关推荐:

    简单介绍PHP 文件锁与进程锁

    php 文件读取系列方法详解

    简单谈谈 php 文件锁

    위 내용은 PHP의 파일 잠금, 뮤텍스 잠금 및 읽기-쓰기 잠금에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿