How to interrupt an execution of a thread from the main context?
In the snippet below - how would one go about stopping the action that the thread does without destroying it?
class ReadFileThread extends Thread
{
public function __construct($file, $chunk = 1024)
{
$this->file = $file;
$this->chunk = $chunk;
}
public function run()
{
if(is_file($this->file) && is_readable($this->file))
{
$fh = fopen($this->file, 'rb');
while(!feof($fh))
{
$content = fread($fh, $this->chunk);
}
fclose($fh);
}
}
}
$num = 10;
$threads = [];
for($i = 0; $i < $num; $i++)
{
$thread = new ReadFileThread('/path/to/10gig_file.txt', 1024);
$threads[] = $thread;
// I start the thread, now it's detached from the main context and is reading the file asynchronously
$thread->start();
}
// The interesting part - I want to random 1 thread whose operation of file reading I want to interrupt
$to_interrupt = $threads[rand(0, $num)];
// How to interrupt the thread without destroying it? I want its context preserved
Pthreads is an object-oriented API which provides a convenient way to organize multi-threaded tasks in PHP. The API includes all the tools needed to create multi-threaded applications. PHP applications can create, read, write, execute and synchronize threads using objects of the classes Thread, Worker and Threaded. What's inside pthreads?
It does understand PHP native functions. -consts: previous constants will transfer over. Don't make any new ones thou! -pThreads only work in CLI - of course! -If a thread crashes it automatically gets recreated. -In order to 'force kill' any thread the parent must be killed.
While a thread is suspended, it is not scheduled for time on the processor. If a thread is created in a suspended state (with the CREATE_SUSPENDED flag), it does not begin to execute until another thread calls the ResumeThread function with a handle to the suspended thread.
-If a thread crashes it automatically gets recreated. -In order to 'force kill' any thread the parent must be killed. Or wait until all other tasks queued are complete and then terminate. -Anything registered in a pThread does not seem to join the main thread ... which is good!
RandomSeeds answer is close, but open to race conditions.
<?php
class FileReader extends Thread {
public $file;
public $pause;
public function __construct($file) {
$this->file = $file;
$this->pause = false;
}
public function run() {
if (($handle = fopen($this->file, "rb"))) {
$len = 0;
do {
$this->synchronized(function(){
if ($this->paused) {
printf(
"\npausing %lu ...\n", $this->getThreadId());
$this->wait();
}
});
$data = fread($handle, 1024);
$len += strlen($data);
if (($len % 2) == 0) {
printf(
"\r\rread %lu", $len);
}
} while (!feof($handle));
fclose($handle);
}
}
public function pause() {
return $this->synchronized(function(){
return ($this->paused = true);
});
}
public function unpause() {
return $this->synchronized(function(){
$this->paused = false;
if ($this->isWaiting()) {
return $this->notify();
}
});
}
}
function do_something($time) {
$start = time();
while (($now = time()) < ($start + $time)) {
usleep(100);
if (($now % 2) == 0) {
echo ".";
}
}
echo "\n";
}
$reader = new FileReader("/path/to/big/file.ext");
$reader->start();
sleep(2);
$reader->pause();
do_something(rand(2, 4));
$reader->unpause();
sleep(2);
$reader->pause();
do_something(rand(2, 4));
$reader->unpause();
sleep(2);
$reader->pause();
do_something(rand(2, 4));
$reader->unpause();
?>
It is important that variables used for purposes of synchronization are only ever access in synchronized blocks, I have omitted the implementation of a stop/quit function but it's logic is much the same, as RandomSeeds example shows.
Race conditions lurk within:
public function mine($data) {
/* anyone can set doSynchronization at any time */
if ($this->doSynchronization) {
$this->synchronize(function(){
/* checking the predicate in here is safer */
$this->wait();
});
}
}
Good:
public function mine($data) {
$this->synchronize(function(){
if ($this->doSynchronization) {
$this->wait();
}
});
}
Extreme:
public function mine($data) {
$this->synchronize(function(){
while ($this->doSynchronization) {
$this->wait();
}
});
}
The posix standard would always have you write it the extreme way, I'm not so fussed, whatever works for you. The reason for this extreme code is, allowances must be made for a thread to receive a signal other than the one it is waiting on, many low level signals may result in a thread waking from a call to pthread_cond_wait; checking the predicate in a loop like that guards against what the specification calls spurious wakeups ... but such extreme measures can also lead to ill side effects; the reason those threads receive the low level signal is because it is necessary for them to take some action of some kind, ignoring that can easily cause a different part of the stack to deadlock because it expected your thread to die (or do something else, die is an example) when it was signalled ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With