1

I'm currently getting familiar with php threads. Found worker and collectable classess pretty intersting and convenient. But I can't find out how to lock variables for changing

class job extends Collectable {
  public static $count = 0;
  public $url;
  private $file = "outfile.txt";
  public function __construct($url){
    // init some properties
    $this->url = $url;

  }
  public function run(){
    // do some work
    //$this->val = $this->val . file_get_contents('http://www.example.com/', null, null, 3, 20);
    //echo $this->url;
    $data = explode("|", $this->url);
    $vars = GetServerVars($data[0], $data[1]);
    $this->lock();
    self::$count++;
    $this->unlock();
    echo "current_count: ".self::$count."\n";
    $this->setGarbage();
  }
}

For some reason this doesn't work and I get number 1,2,3,4 for few times in a row. So self::$count doesn't increment successively. Why this could happen? And what is the way to correct lock variables in pthreads? Thanks!

4
  • You cannot lock variables... Commented Jan 29, 2016 at 18:57
  • @CharlotteDunois, what? That's sad. Are there some other solutions with pthreads to get my code working? Commented Jan 29, 2016 at 19:01
  • I'm not really sure what your issue is. All I know you get some numbers for a few times in a row and your $count variable doesn't increment. What do you even expect to happen? Commented Jan 29, 2016 at 19:03
  • @CharlotteDunois, this count variable should show how many tasks was executed (for example). Is this possible for pthreads to return results? So I would know how many operations failed and how many was a success? Commented Jan 29, 2016 at 19:08

1 Answer 1

5

Static variables are thread local, so you cannot use a static variable as a shared counter.

The following code will create a stupid number of threads (100), each thread accepts a Threaded result object and an int $value.

If $value is an even number we consider it a success, otherwise it is a failure; we execute an appropriate function to increment a shared counter safely.

<?php
class Results extends Threaded {

    public function addError() {
        return $this->synchronized(function(){
            return $this->error++;
        });
    }

    public function addSuccess() {
        return $this->synchronized(function(){
            return $this->success++;
        });
    }

    private $error;
    private $success;
}

class Process extends Thread {

    public function __construct(Results $results, int $value) {
        $this->results = $results;
        $this->value = $value;
    }

    public function run() {
        if ($this->value % 2 == 0)
            $this->results->addSuccess();
        else $this->results->addError();
    }

    private $results;
    private $value;
}

$results   = new Results();
$processes = [];
$process   = 0;

do {
    $processes[$process] = 
        new Process($results, mt_rand(1, 100));
    $processes[$process]->start();
} while (++$process < 100);

foreach ($processes as $process)
    $process->join();

var_dump($results);
?>

Note: this is PHP 7 + pthreads v3 code ... don't use v2 for new projects

Calling synchronized ensures that no other context can enter a synchronized block for the same object while the calling context is, this ensures safety for the operations provided in the synchronized block:

object(Results)#1 (2) {
  ["error"]=>
  int(49)
  ["success"]=>
  int(51)
}

The inquisitive programmer will probably now proceed to removing the synchronized blocks, and, much to their surprise will find that the output is the same.

The reason for this is because operations on objects members are atomic - the increment and decrement instructions can be assumed to be atomic, in other words.

It's too hard for you to guess which operations are going to be atomic, so don't.

Assumptions are horrible; as soon as the code in the synchronized block is more complex than single instructions your code is open to race conditions. The sensible thing to do is set a standard that says if you require atomicity for any number of statements they should be executed in a synchronized block.

Sign up to request clarification or add additional context in comments.

1 Comment

Nice, thanks for explaining. For my case, thats not overloaded with complex operations that's pretty good!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.