Heim  >  Artikel  >  Backend-Entwicklung  >  Ausführliche Erläuterung der Dateisperren, Mutex-Sperren und Lese-/Schreibsperren in PHP

Ausführliche Erläuterung der Dateisperren, Mutex-Sperren und Lese-/Schreibsperren in PHP

*文
*文Original
2017-12-29 18:02:483799Durchsuche

Dieser Artikel stellt hauptsächlich die Analyse von Dateisperren, Mutex-Sperren und Lese-/Schreibsperren in PHP-Programmen vor. Er konzentriert sich auf die Verwendungsbeispiele im Sync-Modul und im Pthreads-Modul. Ich hoffe, es hilft allen.

Dateisperre
Der vollständige Name lautet „Advisory File Lock“ und wird im Buch erwähnt. Diese Art der Sperre ist relativ häufig. Nach dem Start von MySQL und PHP-FPM wird die Prozess-ID in einer PID-Datei aufgezeichnet.

Diese Sperre kann verhindern, dass ein Prozess wiederholt ausgeführt wird. Wenn Sie beispielsweise crontab verwenden, ist die Ausführung einer Aufgabe pro Minute begrenzt, der Prozess kann jedoch länger als eine Minute ausgeführt werden Zur Lösung des Konflikts werden die beiden Prozesse zusammen ausgeführt. Es treten Probleme bei der Ausführung auf.

Ein weiterer Vorteil der Verwendung der PID-Dateisperre besteht darin, dass der Prozess bequem Stopp- oder Neustartsignale an sich selbst senden kann. Der Befehl zum Neustart von php-fpm lautet beispielsweise

kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`
Senden Sie das USR2-Signal an den aufgezeichneten Prozess in der PID-Datei. Es gehört zur Prozesskommunikation und wird in einem anderen Kapitel geöffnet.

Die Schnittstelle von PHP ist Flock und die Dokumentation ist relativ detailliert. Werfen wir zunächst einen Blick auf die Definition: bool flock ( resources $handle , int $operation [, int &$wouldblock ] ).

  • $handle ist der Dateisystemzeiger Wird normalerweise von fopen() verwendet. Die erstellte Ressource. Das bedeutet, dass eine Datei geöffnet werden muss, um Flock nutzen zu können.

  • $operation ist der Operationstyp.

  • &$wouldblock Wenn die Sperre blockiert, wird diese Variable auf 1 gesetzt.

Es ist zu beachten, dass diese Funktion standardmäßig verwendet wird Wenn Sie nicht blockieren möchten, können Sie der Operation eine Bitmaske LOCK_NB hinzufügen.


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

Speichern Sie es als Process.php, führen Sie php Process.php aus und führen Sie dann php Process.php erneut aus, und Sie sehen die Fehlermeldung. flock hat auch gemeinsame Sperren, LOCK_SH.

Mutex-Sperren und Lese-/Schreibsperren
Mutex im Synchronisierungsmodul:
Mutex ist ein zusammengesetztes Wort, gegenseitiger Ausschluss. Verwenden Sie pecl, um das Synchronisierungsmodul zu installieren, pecl install sync. Der SyncMutex im Dokument verfügt nur über zwei Methoden: Sperren und Entsperren. Gehen wir direkt zum Codetest. Ich habe es nicht in der IDE geschrieben, daher ist das CS extrem hässlich, bitte ignorieren Sie es.


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

als mutex.php speichern, php mutex.php ausführen, Ausgabe ist


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

Hier stehen die Teilprozesse 0 und 1 nicht unbedingt im Vordergrund. Aber es gibt immer jemanden, der das Schloss nicht bekommt. Der Parameter von SyncMutex::lock(int $millisekunde) ist hier Millisekunde, was die Blockierungsdauer darstellt, und -1 bedeutet unendliche Blockierung.

Lese-/Schreibsperre im Synchronisierungsmodul:
Die Methode von SyncReaderWriter ist ähnlich: Lesesperre, Leseunsperre, Schreibsperre, Schreibunsperre, sie können paarweise erscheinen, es wird kein Testcode geschrieben , es sollte derselbe sein wie Mutex Der Code ist derselbe, ersetzen Sie einfach die Sperre.

Event im Sync-Modul:
Es fühlt sich eher so an, als ob Cond in Golang, wait() blockiert und fire() einen durch Event blockierten Prozess aufweckt. Es gibt einen guten Artikel, in dem Cond vorgestellt wird. Es ist ersichtlich, dass Cond eine feste Verwendung von Sperren ist. Das Gleiche gilt für SyncEvent.
Das Beispiel in der PHP-Dokumentation zeigt, dass die Methode fire() offenbar in Webanwendungen verwendet wird.

Testcode auf

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

Hier schreiben wir absichtlich ein fire() weniger, damit das Programm blockiert, was beweist, dass fire() jeweils nur einen Prozess aufweckt.

pthreads-Modul
Mutexe sperren und entsperren:

Funktionen:


pthread_mutex_lock (mutex) 
pthread_mutex_trylock (mutex) 
pthread_mutex_unlock (mutex)

Verwendung:

Der Thread verwendet die Funktion pthread_mutex_lock(), um die angegebene Mutex-Variable zu sperren. Wenn der Mutex bereits von einem anderen Thread gesperrt ist, blockiert dieser Aufruf den Thread, bis der Mutex entsperrt wird.
pthread_mutex_trylock() versucht, einen Mutex zu sperren, wenn der Mutex jedoch bereits gesperrt ist, kehrt die Routine sofort mit einem „Beschäftigt“-Fehlercode zurück. Diese Routine kann in pthread_mutex_trylock() nützlich sein.

Versuchen Sie, einen Mutex zu sperren. Wenn der Mutex jedoch bereits gesperrt ist, kehrt das Programm sofort mit einem Busy-Fehlerwert zurück. Diese Funktion ist nützlich, um einen Deadlock bei Prioritätsänderungen zu verhindern. Ein Thread kann pthread_mutex_unlock() verwenden, um den von ihm belegten Mutex zu entsperren. Diese Funktion kann aufgerufen werden, wenn ein Thread die Verwendung geschützter Daten abschließt und andere Threads einen Mutex erhalten möchten, um an den geschützten Daten zu arbeiten. Ein Fehler tritt auf, wenn die folgende Situation auftritt:

  • Der Mutex wurde entsperrt

  • Der Mutex ist von einem anderen Thread belegt

Es gibt nichts „Magisches“ an Mutexes. Tatsächlich sind sie das „Gentleman's Agreement“ der teilnehmenden Threads. Achten Sie beim Schreiben von Code darauf, Mutexe korrekt zu sperren und zu entsperren.

F: Es warten mehrere Threads auf denselben gesperrten Mutex. Wenn der Mutex entsperrt ist, welcher Thread wird den Mutex als erster sperren?
A: Sofern der Thread nicht den Prioritätsplanungsmechanismus verwendet, wird der Thread vom Systemplaner zugewiesen und der Thread, der den Mutex zuerst sperrt, ist zufällig.


#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 文件锁

Das obige ist der detaillierte Inhalt vonAusführliche Erläuterung der Dateisperren, Mutex-Sperren und Lese-/Schreibsperren in PHP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn