1
0
mirror of https://github.com/danog/parallel.git synced 2024-11-30 04:39:01 +01:00
Go to file
2015-08-27 14:49:41 -05:00
benchmarks Add benchmark for socket pairs 2015-08-03 00:28:38 -05:00
bin Refine Process, context and worker updates 2015-08-27 13:06:39 -05:00
examples Work on workers, tasks, and contexts 2015-08-27 09:10:08 -05:00
src Fix bug with static closures 2015-08-27 14:49:41 -05:00
tests Fix parcel method name errors and tests 2015-08-24 23:23:42 -05:00
.gitattributes Add benchmarks for profiling implementations and backends 2015-07-16 03:02:58 -05:00
.gitignore Add vagrant to gitignore 2015-08-02 19:45:44 -05:00
composer.json Do not call fclose() in thread; separate read/write streams; addresses #4 2015-08-24 19:35:42 -05:00
CONTRIBUTING.md Initial commit 2015-07-10 15:15:42 -05:00
LICENSE Initial commit 2015-07-10 15:15:42 -05:00
phpdoc.dist.xml Initial commit 2015-07-10 15:15:42 -05:00
phpunit.xml.dist Merge back into one test suite 2015-08-05 11:29:22 -05:00
README.md Update examples in README 2015-08-24 13:56:30 -05:00
Vagrantfile Update Vagrant box to use pthreads latest 2015-08-22 01:02:41 -05:00

Concurrency Component for Icicle

Under development -- keep an eye out for things to come in the near future though!

Concurrent provides a means of parallelizing code without littering your application with complicated lock checking and inter-process communication.

To be as flexible as possible, Concurrent comes with a collection of non-blocking concurrency tools that can be used independently as needed, as well as an "opinionated" task API that allows you to assign units of work to a pool of worker threads or processes.

Requirements

  • PHP 5.5+
  • pthreads for multithreading or
  • System V-compatible Unix OS and PHP with --enable-pcntl

Benchmarks

A few benchmarks are provided for analysis and study. Can be used to back up implementation decisions, or to measure performance on different platforms or hardware.

vendor/bin/athletic -p benchmarks -b vendor/autoload.php

Documentation

Concurrent can use either process forking or true threading to parallelize execution. Threading provides better performance and is compatible with Unix and Windows but requires ZTS (Zend thread-safe) PHP, while forking has no external dependencies but is only compatible with Unix systems. If your environment works meets neither of these requirements, this library won't work.

Threads

Threading is a cross-platform concurrency method that is fast and memory efficient. Thread contexts take advantage of an operating system's multi-threading capabilities to run code in parallel. A spawned thread will run completely parallel to the parent thread, each with its own environment. Each thread is assigned a closure to execute when it is created, and the returned value is passed back to the parent thread. Concurrent goes for a "shared-nothing" architecture, so any variables inside the closure are local to that thread and can store any non-safe data.

You can spawn a new thread with the Thread::spawn() method:

use Icicle\Concurrent\Threading\Thread;
use Icicle\Coroutine;
use Icicle\Loop;

Coroutine\create(function () {
    $thread = Thread::spawn(function () {
        print "Hello, World!\n";
    });

    yield $thread->join();
});

Loop\run();

You can wait for a thread to finish by calling join(). Joining does not block the parent thread and will asynchronously wait for the child thread to finish before resolving.

Forks

For Unix-like systems, you can create parallel execution using fork contexts. Though not as efficient as multi-threading, in some cases forking can take better advantage of some multi-core processors than threads. Fork contexts use the pcntl_fork() function to create a copy of the current process and run alternate code inside the new process.

Spawning and controlling forks are quite similar to creating threads. To spawn a new fork, use the Fork::spawn() method:

use Icicle\Concurrent\Forking\Fork;
use Icicle\Coroutine;
use Icicle\Loop;

Coroutine\create(function () {
    $fork = Fork::spawn(function () {
        print "Hello, World!\n";
    });

    yield $fork->join();
});

Loop\run();

Calling join() on a fork will asynchronously wait for the forked process to terminate, similar to the pcntl_wait() function.

Synchronization with channels

Threads and forks wouldn't be very useful if they couldn't be given any data to work on. The recommended way to share data between contexts is with a Channel. A channel is a low-level abstraction over local, non-blocking sockets, which can be used to pass messages and objects between two contexts. Channels are non-blocking and do not require locking. For example:

use Icicle\Concurrent\Sync\Channel;
use Icicle\Concurrent\Threading\Thread;
use Icicle\Coroutine;
use Icicle\Loop;

Coroutine\create(function () {
    list($socketA, $socketB) = Channel::createSocketPair();
    $channel = new Channel($socketA);

    $thread = Thread::spawn(function ($socketB) {
        $channel = new Channel($socketB);
        yield $channel->send("Hello!");
    }, $socketB);

    $message = (yield $channel->receive());
    yield $thread->join();
});

Loop\run();

Synchronization with parcels

Parcels are shared containers that allow you to store context-safe data inside a shared location so that it can be accessed by multiple contexts. To prevent race conditions, you still need to access a parcel's data exclusively, but Concurrent allows you to acquire a lock on a parcel asynchronously without blocking the context execution, unlike traditional mutexes.

License

All documentation and source code is licensed under the Apache License, Version 2.0 (Apache-2.0). See the LICENSE file for details.