파일 잠금
전체 이름은 책에서 언급한 권고 파일 잠금입니다. 이러한 유형의 잠금은 비교적 일반적입니다. 예를 들어, mysql과 php-fpm이 시작된 후에는 프로세스 ID를 기록하는 pid 파일이 있습니다.
이 잠금을 사용하면 프로세스가 반복적으로 실행되는 것을 방지할 수 있습니다. 예를 들어 crontab을 사용하면 1분마다 하나의 작업이 실행되도록 제한되지만 프로세스 잠금을 사용하여 해결하지 않으면 이 프로세스가 1분 이상 실행될 수 있습니다. 충돌이 발생하면 두 프로세스가 함께 실행되는 데 문제가 있습니다.
PID 파일 잠금을 사용하면 프로세스가 중지 또는 다시 시작 신호를 자체적으로 보내는 것이 편리하다는 또 다른 이점이 있습니다. 예를 들어 php-fpm을 다시 시작하는 명령은
입니다.kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`
USR2 신호를 pid 파일에 기록된 프로세스로 보냅니다. 신호는 프로세스 통신에 속하며 다른 장에서 설명합니다.
PHP의 인터페이스는 매우 다양하며 문서는 비교적 자세합니다. 먼저 정의를 살펴보겠습니다. bool Flock (resource $handle , int $Operation [, int &$wouldblock ] ).
이 함수는 기본적으로 차단된다는 점에 유의해야 합니다. 비차단을 원할 경우 비트마스크 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도 있습니다.
뮤텍스 잠금 및 읽기/쓰기 잠금
동기화 모듈의 뮤텍스:
뮤텍스는 상호배제, 상호배제의 합성어입니다. 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과 유사하며, 작성된 테스트 코드가 없으며 Mutex 코드와 일치해야 하며 잠금만 교체하면 됩니다.
동기화 모듈의 이벤트:
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(); }
여기에는 의도적으로 One fire()를 작성하여 프로그램이 차단됩니다. 이는 fire()가 한 번에 하나의 프로세스만 깨운다는 것을 증명합니다.
pthread 모듈
뮤텍스 잠금 및 잠금 해제:
기능:
pthread_mutex_lock (mutex) pthread_mutex_trylock (mutex) pthread_mutex_unlock (mutex)
사용법:
스레드는 pthread_mutex_lock() 함수를 사용하여 지정된 뮤텍스 변수를 잠급니다. 다른 스레드가 뮤텍스를 이미 잠근 경우 이 호출은 뮤텍스가 잠금 해제될 때까지 스레드를 차단합니다.
pthread_mutex_trylock()은 뮤텍스를 잠그려고 시도합니다. 그러나 뮤텍스가 이미 잠겨 있으면 루틴은 "busy" 오류 코드와 함께 즉시 반환됩니다. 이 루틴은 pthread_mutex_trylock()에서 유용할 수 있습니다.
뮤텍스를 잠그려고 시도합니다. 그러나 뮤텍스가 이미 잠긴 경우 프로그램은 즉시 사용 중 오류 값을 반환합니다. 이 기능은 우선순위 변경 시 교착상태를 방지하는 데 유용합니다. 스레드는 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(); }
这时候两个进程都能得到锁。
$key = ftok('/tmp', 'c'); $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