2012-03-13 67 views
7

Tôi có một chức năng cần phải đi qua khoảng 20K hàng từ một mảng và áp dụng một tập lệnh bên ngoài cho từng mảng. Đây là một quá trình chậm, như PHP đang chờ cho kịch bản được thực hiện trước khi tiếp tục với hàng tiếp theo.Thực hiện các chức năng song song

Để làm cho quy trình này nhanh hơn, tôi đã nghĩ đến việc chạy chức năng ở các phần khác nhau, cùng một lúc. Vì vậy, ví dụ, các hàng từ 0 đến 2000 là một hàm, từ 2001 đến 4000 trên một hàm khác, v.v. Làm thế nào tôi có thể làm điều này một cách gọn gàng? Tôi có thể thực hiện các công việc cron khác nhau, một cho mỗi chức năng với các thông số khác nhau: myFunction(0, 2000), sau đó một công việc cron khác với myFunction(2001, 4000), v.v. nhưng điều đó dường như không quá rõ ràng. Cách tốt để làm điều này là gì?

+1

Tôi đã ở trong tình huống tương tự một lần. Tôi thấy rằng forking là khá đau đớn trong php vì vậy tôi đã làm nó trong bash. đã vượt qua các đối số khởi động và dừng cho từng phiên bản của tập lệnh và chạy chúng trong nền trên máy chủ. Exec là những gì bạn đang tìm kiếm tôi tin. – Jake

Trả lời

6

Nếu bạn muốn thực hiện các tác vụ song song trong PHP, tôi sẽ xem xét sử dụng Gearman. Một cách tiếp cận khác sẽ là sử dụng pcntl_fork(), nhưng tôi thích công nhân thực tế hơn khi nó dựa trên nhiệm vụ.

+0

Bạn đề xuất tôi nên làm gì nếu tôi không có quyền truy cập vào cấu hình php (Im 'trên máy chủ dùng chung)? – Oliver

0

Hãy xem pcntl_fork. Điều này cho phép bạn sinh ra các tiến trình con mà sau đó có thể thực hiện công việc riêng biệt mà bạn cần.

0

Không chắc chắn nếu một giải pháp cho tình huống của bạn nhưng bạn có thể chuyển hướng đầu ra của các cuộc gọi hệ thống đến một tệp, do đó PHP sẽ không đợi cho đến khi chương trình kết thúc. Mặc dù điều này có thể dẫn đến quá tải máy chủ của bạn.

http://www.php.net/manual/en/function.exec.php - Nếu chương trình được bắt đầu bằng chức năng này, để chương trình tiếp tục chạy dưới nền, đầu ra của chương trình phải được chuyển hướng đến một tệp hoặc luồng đầu ra khác. Không làm như vậy sẽ khiến PHP bị treo cho đến khi chương trình kết thúc.

+0

Liên kết đã qua đời. –

+0

liên kết được cập nhật @DaveCarruthers – Michal

6

Thời gian chờ đợi duy nhất bạn gặp phải là nhận dữ liệu và xử lý dữ liệu. Xử lý dữ liệu thực sự là hoàn toàn ngăn chặn anyway (bạn chỉ đơn giản là phải chờ đợi cho nó). Bạn sẽ không có khả năng nhận được bất kỳ lợi ích nào trong quá khứ khi tăng số lượng quy trình lên số lõi mà bạn có. Về cơ bản tôi nghĩ rằng điều này có nghĩa là số lượng các quy trình nhỏ nên việc lên lịch thực hiện các quy trình 2-8 không có vẻ ghê tởm. Nếu bạn lo lắng về việc không thể xử lý dữ liệu trong khi truy xuất dữ liệu, bạn có thể nhận được dữ liệu từ cơ sở dữ liệu trong các khối nhỏ và sau đó phân phối tải xử lý giữa một vài quy trình, một cho mỗi lõi.

Tôi nghĩ rằng tôi căn chỉnh nhiều hơn với phương pháp xử lý con forking để thực sự chạy các luồng xử lý. Có là một minh chứng rực rỡ trong các ý kiến ​​trên trang doc pcntl_fork cho thấy một thực hiện của một lớp daemon việc

http://php.net/manual/en/function.pcntl-fork.php

<?php 
declare(ticks=1); 
//A very basic job daemon that you can extend to your needs. 
class JobDaemon{ 

    public $maxProcesses = 25; 
    protected $jobsStarted = 0; 
    protected $currentJobs = array(); 
    protected $signalQueue=array(); 
    protected $parentPID; 

    public function __construct(){ 
     echo "constructed \n"; 
     $this->parentPID = getmypid(); 
     pcntl_signal(SIGCHLD, array($this, "childSignalHandler")); 
    } 

    /** 
    * Run the Daemon 
    */ 
    public function run(){ 
     echo "Running \n"; 
     for($i=0; $i<10000; $i++){ 
      $jobID = rand(0,10000000000000); 

      while(count($this->currentJobs) >= $this->maxProcesses){ 
       echo "Maximum children allowed, waiting...\n"; 
       sleep(1); 
      } 

      $launched = $this->launchJob($jobID); 
     } 

     //Wait for child processes to finish before exiting here 
     while(count($this->currentJobs)){ 
      echo "Waiting for current jobs to finish... \n"; 
      sleep(1); 
     } 
    } 

    /** 
    * Launch a job from the job queue 
    */ 
    protected function launchJob($jobID){ 
     $pid = pcntl_fork(); 
     if($pid == -1){ 
      //Problem launching the job 
      error_log('Could not launch new job, exiting'); 
      return false; 
     } 
     else if ($pid){ 
      // Parent process 
      // Sometimes you can receive a signal to the childSignalHandler function before this code executes if 
      // the child script executes quickly enough! 
      // 
      $this->currentJobs[$pid] = $jobID; 

      // In the event that a signal for this pid was caught before we get here, it will be in our signalQueue array 
      // So let's go ahead and process it now as if we'd just received the signal 
      if(isset($this->signalQueue[$pid])){ 
       echo "found $pid in the signal queue, processing it now \n"; 
       $this->childSignalHandler(SIGCHLD, $pid, $this->signalQueue[$pid]); 
       unset($this->signalQueue[$pid]); 
      } 
     } 
     else{ 
      //Forked child, do your deeds.... 
      $exitStatus = 0; //Error code if you need to or whatever 
      echo "Doing something fun in pid ".getmypid()."\n"; 
      exit($exitStatus); 
     } 
     return true; 
    } 

    public function childSignalHandler($signo, $pid=null, $status=null){ 

     //If no pid is provided, that means we're getting the signal from the system. Let's figure out 
     //which child process ended 
     if(!$pid){ 
      $pid = pcntl_waitpid(-1, $status, WNOHANG); 
     } 

     //Make sure we get all of the exited children 
     while($pid > 0){ 
      if($pid && isset($this->currentJobs[$pid])){ 
       $exitCode = pcntl_wexitstatus($status); 
       if($exitCode != 0){ 
        echo "$pid exited with status ".$exitCode."\n"; 
       } 
       unset($this->currentJobs[$pid]); 
      } 
      else if($pid){ 
       //Oh no, our job has finished before this parent process could even note that it had been launched! 
       //Let's make note of it and handle it when the parent process is ready for it 
       echo "..... Adding $pid to the signal queue ..... \n"; 
       $this->signalQueue[$pid] = $status; 
      } 
      $pid = pcntl_waitpid(-1, $status, WNOHANG); 
     } 
     return true; 
    } 
} 
2

bạn có thể sử dụng "pthreads"

rất dễ dàng để cài đặt và hoạt động tuyệt vời trên cửa sổ

tải về từ đây ->http://windows.php.net/downloads/pecl/releases/pthreads/2.0.4/

Giải nén file zip và sau đó

  • di chuyển tệp 'php_pthreads.dll' sang thư mục php \ ext \.

  • di chuyển tệp 'pthreadVC2.dll' sang thư mục php \.

sau đó thêm dòng này trong tập tin 'php.ini' của bạn:

extension=php_pthreads.dll 

lưu các tập tin.

bạn chỉ cần thực hiện :-)

bây giờ cho phép xem ví dụ về cách sử dụng nó:

class ChildThread extends Thread { 
    public $data; 

    public function run() { 
     /* Do some expensive work */ 

     $this->data = 'result of expensive work'; 
    } 
} 

$thread = new ChildThread(); 

if ($thread->start()) {  
    /* 
    * Do some expensive work, while already doing other 
    * work in the child thread. 
    */ 

    // wait until thread is finished 
    $thread->join(); 

    // we can now even access $thread->data 
} 

để biết thêm thông tin về pthreads đọc tài liệu php ở đây:

PHP DOCS PTHREADS

  • nếu you'r sử dụng WAMP như tôi, thì bạn nên thêm 'pthreadVC2.dll' vào \ wamp \ bin \ apache \ ApacheX.XX \ bin và cũng chỉnh sửa các tập tin 'php.ini' (cùng đường) và thêm dòng giống như trước

    extension = php_pthreads.dll

GOOD LUCK!

Các vấn đề liên quan