2012-01-31 10 views
9

Estoy tratando de escribir una secuencia de comandos PHP que quiero asegurarme de que solo se ejecuta una instancia única en un momento dado. Toda esta charla sobre las diferentes formas de bloqueo, las condiciones de carrera, etc., etc., me está poniendo nervioso.PHP bloqueando/asegurándose de que una secuencia de comandos determinada solo se ejecuta una vez en un momento dado

estoy confundido en cuanto a si los ficheros de bloqueo son el camino a seguir, o semáforos, o el uso de bloqueos de MySQL, o etc etc etc

Puede alguien decirme:

a) ¿Qué es el forma correcta para implementar esto?

Y

b) que me señale un aplicación PHP (o algo fácil de portar a PHP?)

+2

es el script ejecuta en un servidor web, o la línea de comandos? –

+0

Si realmente importa ... servidor web. –

+1

probablemente un bloqueo de mysql, en ese caso, por lo que el script puede abortar con gracia y decir "ya está en uso". usar flock y similares bloquearía el script fuera de apache y probablemente causaría 500 errores internos y otras cosas. –

Respuesta

0

Usted puede ir para la solución que mejor se adapte a su proyecto, las dos formas sencillas para lograr ese son bloqueo de archivos o bloqueo de bases de datos.

Para implementaciones de bloqueo de archivos, compruebe http://us2.php.net/flock

Si ya utiliza una base de datos, crear una tabla, generar símbolo conocido por ese guión, lo puso allí, y simplemente retirarla después del final de la secuencia de comandos. Para evitar problemas en los errores, puede usar los tiempos de caducidad.

+0

¿Estás seguro de que esta es una forma segura de manejarlo? Los comentarios en PHP.net tienen varias entradas que indican que puede introducir condiciones de carrera. –

1
// borrow from 2 anwsers on stackoverflow 
function IsProcessRunning($pid) { 
    return shell_exec("ps aux | grep " . $pid . " | wc -l") > 2; 
} 

function AmIRunning($process_file) { 
    // Check I am running from the command line 
    if (PHP_SAPI != 'cli') { 
     error('Run me from the command line'); 
     exit; 
    } 

    // Check if I'm already running and kill myself off if I am 
    $pid_running = false; 
    $pid = 0; 
    if (file_exists($process_file)) { 
     $data = file($process_file); 
     foreach ($data as $pid) { 
      $pid = (int)$pid; 
      if ($pid > 0 && IsProcessRunning($pid)) { 
       $pid_running = $pid; 
       break; 
      } 
     } 
    } 
    if ($pid_running && $pid_running != getmypid()) { 
     if (file_exists($process_file)) { 
      file_put_contents($process_file, $pid); 
     } 
     info('I am already running as pid ' . $pid . ' so stopping now'); 
     return true; 
    } else { 
     // Make sure file has just me in it 
     file_put_contents($process_file, getmypid()); 
     info('Written pid with id '.getmypid()); 
     return false; 
    } 
} 

/* 
* Make sure there is only one instance running at a time 
*/ 
$lockdir = '/data/lock'; 
$script_name = basename(__FILE__, '.php'); 
// The file to store our process file 
$process_file = $lockdir . DS . $script_name . '.pid'; 

$am_i_running = AmIRunning($process_file); 
if ($am_i_running) { 
    exit; 
} 
0

En caso de que usted está usando PHP en Linux y creo que la forma más práctica es:

<?php 
if(shell_exec('ps aux | grep '.__FILE__.' | wc -l')>3){ 
    exit('already running...'); 
} 
?> 

Otra forma de hacerlo es con indicador de archivo y devolución de llamada de salida, la devolución de llamada de salida se asegurará de que el indicador de archivo se restablecerá a 0 en cualquier caso de ejecución php final también fata l errores

<?php 
function exitProcess(){ 
    if(file_get_contents('inprocess.txt')!='0'){ 
    file_put_contents('inprocess.txt','0'); 
    } 
} 

if(file_get_contents('inprocess.txt')=='1'){ 
    exit(); 
} 

file_put_contents('inprocess.txt','1'); 
register_shutdown_function('exitProcess'); 

/** 
execute stuff 
**/ 
?> 
0

Una forma es utilizar la función de PHP rebaño con un archivo ficticio, que actuará como organismo de control.

En el comienzo de nuestro trabajo, si el archivo de elevar una LOCK_EX bandera, salida, o esperar, se puede hacer.

PHP documentación rebaño: http://php.net/manual/en/function.flock.php

Para estos ejemplos, un archivo llamado lock.txt se debe crear en primer lugar.

Ejemplo 1, si se está ejecutando otro proceso gemelo, se cerrará correctamente, sin volver a intentar, dando un mensaje de estado.

arrojará el estado de error, si el archivo lock.txt no es accesible.

<?php 

$fp = fopen("lock.txt", "r+"); 

if (!flock($fp, LOCK_EX|LOCK_NB, $blocked)) { 
    if ($blocked) { 

     // another process holds the lock 
     echo "Couldn't get the lock! Other script in run!\n"; 

    } 
    else { 
     // couldn't lock for another reason, e.g. no such file 
     echo "Error! Nothing done."; 
    } 
} 
else { 
    // lock obtained 
    ftruncate($fp, 0); // truncate file 

    // Your job here 
    echo "Job running!\n"; 

    // Leave a breathe 
    sleep(3); 

    fflush($fp);   // flush output before releasing the lock 
    flock($fp, LOCK_UN); // release the lock 

} 

fclose($fp); // Empty memory 

Ejemplo 2, se quiere que el proceso para esperar, para una ejecución después de la cola, en su caso:

<?php 

$fp = fopen("lock.txt", "r+"); 

if (flock($fp, LOCK_EX)) { // acquire an exclusive lock 
    ftruncate($fp, 0);  // truncate file 

    // Your job here 
    echo "Job running!\n"; 

    // Leave a breathe 
    sleep(3); 

    fflush($fp);   // flush output before releasing the lock 
    flock($fp, LOCK_UN); // release the lock 
} 

fclose($fp); 

También es factible con fopen en x modo, creando y borrando un archivo cuando termina el script.

Crear y abrir para escribir solo; coloque el puntero del archivo en el comienzo del archivo . Si el archivo ya existe, los fopen() llaman fallará devolviendo FALSE

http://php.net/manual/en/function.fopen.php

Cuestiones relacionadas