Maison  >  Article  >  développement back-end  >  Explication détaillée des verrous de fichiers, des verrous mutex et des verrous en lecture-écriture en PHP

Explication détaillée des verrous de fichiers, des verrous mutex et des verrous en lecture-écriture en PHP

*文
*文original
2017-12-29 18:02:483724parcourir

Cet article présente principalement l'analyse des verrous de fichiers, des verrous mutex et des verrous en lecture-écriture dans les programmes PHP. Il se concentre sur les exemples d'utilisation dans le module sync et le module pthreads. Les amis dans le besoin peuvent s'y référer. J'espère que cela aide tout le monde.

Verrouillage de fichiers
Le nom complet est le verrouillage de fichiers consultatif, qui est mentionné dans le livre. Ce type de verrouillage est relativement courant. Par exemple, après le démarrage de mysql et php-fpm, il y aura un fichier pid enregistrant l'ID du processus. Ce fichier est le verrouillage du fichier.

Ce verrou peut empêcher un processus de s'exécuter de manière répétée. Par exemple, lors de l'utilisation de crontab, une tâche est limitée à être exécutée toutes les minutes, mais le processus peut s'exécuter pendant plus d'une minute si le verrouillage du processus n'est pas activé. utilisé pour résoudre le conflit, les deux processus seront ensemble Il y aura des problèmes d'exécution.

Un autre avantage de l'utilisation du verrouillage de fichier PID est qu'il est pratique pour le processus de s'envoyer des signaux d'arrêt ou de redémarrage. Par exemple, la commande pour redémarrer php-fpm est

kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`
Envoyer le signal USR2 au processus enregistré dans le fichier pid. Le signal Il appartient à la communication de processus et sera ouvert dans un autre chapitre.

L'interface de PHP est floquée, et la documentation est relativement détaillée. Jetons d'abord un coup d'œil à la définition, bool flock ( resource $handle , int $operation [, int &$wouldblock ] ).

  • $handle est le pointeur du système de fichiers, qui est généralement utilisé par fopen() La ressource créée. Cela signifie qu'un fichier doit être ouvert pour utiliser flock.

  • $opération est le type d'opération.

  • &$wouldblock Si le verrou bloque, alors cette variable sera définie sur 1.

Il est à noter que cette fonction est par défaut au blocage Si vous souhaitez être non bloquant, vous pouvez ajouter un masque de bits LOCK_NB à l'opération. Ensuite, testez-le.


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

Enregistrez-le sous process.php, exécutez php process.php &, puis exécutez à nouveau php process.php et vous verrez le message d'erreur. flock a également des verrous partagés, LOCK_SH.

verrous mutex et verrous en lecture-écriture
Mutex dans le module de synchronisation :
Mutex est un mot composé, exclusion mutuelle. Utilisez pecl pour installer le module de synchronisation, pecl install sync. Le SyncMutex dans le document n'a que deux méthodes, verrouiller et déverrouiller. Passons directement au test du code. Je ne l'ai pas écrit dans l'IDE, donc le cs est extrêmement moche, veuillez l'ignorer.


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

enregistrez sous mutex.php, exécutez php mutex.php, la sortie est


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

Ici, les sous-processus 0 et 1 ne sont pas nécessairement devant. Mais il y en a toujours un qui ne parvient pas à obtenir la serrure. Le paramètre de SyncMutex::lock(int $millisecond) ici est la milliseconde, qui représente la durée du blocage, et -1 signifie un blocage infini.

Verrouillage lecture-écriture dans le module de synchronisation :
La méthode de SyncReaderWriter est similaire, readlock, readunlock, writelock, writeunlock, ils peuvent apparaître par paires, aucun code de test n'est écrit , ça devrait être le même que Mutex. Le code est le même, remplacez simplement le verrou.

Event in sync module :
Cela ressemble plus à Cond in golang, wait() bloque et fire() réveille un processus bloqué par Event. Il existe un bon article présentant Cond. On peut voir que Cond est une utilisation fixe des verrous. Il en va de même pour SyncEvent.
L'exemple de la documentation php montre que la méthode fire() semble être utilisée dans les applications web.

Code de test sur

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

Ici, nous écrivons délibérément un fire() de moins, donc le programme se bloquera, prouvant que fire() ne réveille qu'un processus à la fois.

module pthreads
Verrouillage et déverrouillage des mutex :

Fonctions :


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

Utilisation :

Le thread utilise la fonction pthread_mutex_lock() pour verrouiller la variable mutex spécifiée. Si le mutex est déjà verrouillé par un autre thread, cet appel bloquera le thread jusqu'à ce que le mutex soit déverrouillé.
pthread_mutex_trylock() tentera de verrouiller un mutex. Cependant, si le mutex est déjà verrouillé, la routine reviendra immédiatement avec un code d'erreur "occupé". Cette routine peut être utile dans pthread_mutex_trylock().

Tentative de verrouillage d'un mutex Cependant, si le mutex est déjà verrouillé, le programme retournera immédiatement avec une valeur d'erreur occupée. Cette fonction est utile pour éviter les blocages en cas de changements de priorité. Un thread peut utiliser pthread_mutex_unlock() pour déverrouiller le mutex qu'il occupe. Cette fonction peut être appelée lorsqu'un thread termine l'utilisation des données protégées et que d'autres threads souhaitent obtenir un mutex pour travailler sur les données protégées. Une erreur se produira si la situation suivante se produit :

  • Le mutex a été déverrouillé

  • Le mutex est occupé par un autre thread

Il n'y a rien de « magique » dans les mutex. En fait, ils sont le « gentleman's Agreement » des discussions participantes. Lorsque vous écrivez du code, assurez-vous de verrouiller et déverrouiller correctement les mutex.

Q : Il y a plusieurs threads qui attendent le même mutex verrouillé Lorsque le mutex est déverrouillé, quel thread sera le premier à verrouiller le mutex ?

R : À moins que le thread n'utilise le mécanisme de planification prioritaire, le thread sera alloué par le planificateur système et le thread qui verrouillera le mutex en premier est aléatoire.


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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn