in reply to IPC:Shareable: Not an array reference

Hi Bloehdian,

Re: I want to share an array between two processes created by fork()

The following demonstration provides a cross platform solution. MCE::Shared supports Unix (e.g: AIX, Darwin, FreeBSD, Linux, NetBSD, OpenBSD, Solaris, and possibly others), Windows (ActiveState/Strawberry Perl), and Cygwin. Also supported is Perl included with git-shell for Windows.

use strict; use warnings; use MCE::Shared; tie my @data, 'MCE::Shared'; if ( ! defined ( my $pid = fork() ) ) { die "Cannot fork!: $!"; } elsif ( $pid == 0 ) { # Child my $item; sleep 1; for ( my $i = 1; $i <= 10; $i++ ) { $item = shift @data; $item = '' if ( ! defined $item ); print "Child: $item\n"; } } else { # Parent for ( my $i = 1; $i <= 10; $i++ ) { push @data, $i; print "Parent: @data\n"; } waitpid( $pid, 0 ); }

The snippet produces the following output.

Parent: 1 Parent: 1 2 Parent: 1 2 3 Parent: 1 2 3 4 Parent: 1 2 3 4 5 Parent: 1 2 3 4 5 6 Parent: 1 2 3 4 5 6 7 Parent: 1 2 3 4 5 6 7 8 Parent: 1 2 3 4 5 6 7 8 9 Parent: 1 2 3 4 5 6 7 8 9 10 Child: 1 Child: 2 Child: 3 Child: 4 Child: 5 Child: 6 Child: 7 Child: 8 Child: 9 Child: 10

Regards, Mario.

Replies are listed 'Best First'.
Re^2: IPC:Shareable: Not an array reference
by marioroy (Prior) on Oct 11, 2016 at 03:02 UTC

    Calling MCE::Shared->init() is recommended whenever running many workers simultaneously. This is a one-time call which makes a request to the shared-manager process for a data channel to use for IPC. Init is called automatically by MCE workers so not necessary at the application level.

    Init is helpful for extra performance and applies to running with many workers only.

    elsif ( $pid == 0 ) { # Child MCE::Shared->init(); ... }

    Regards, Mario.

      Hello Mario,

      THX for the reply.

      I checked the documentation on CPAN, but it is not totally clear to me, how I would implement locking with this module. In the actual application, both processes will manipulate @data.

      Cheers

      Bloehdian

        MCE::Shared provides a single point of entry for commands sent to the shared manager process no matter the number of workers. This allows zero locking at the application level. The multiple data channels behind MCE::Shared is for reducing IPC latency. With that in mind, accessing the shared data is better done via the OO methods. The TIE interface may require locking and possible with MCE::Mutex.

        OO interface.

        use strict; use warnings; use MCE::Shared; my $data = MCE::Shared->array(); if ( ! defined ( my $pid = fork() ) ) { die "Cannot fork!: $!"; } elsif ( $pid == 0 ) { # Child MCE::Shared->init(); my $item; sleep 1; for ( my $i = 1; $i <= 10; $i++ ) { $item = $data->shift(); $item = '' if ( ! defined $item ); print "Child: $item\n"; } } else { # Parent for ( my $i = 1; $i <= 10; $i++ ) { $data->push($i); print "Parent: ", join(" ", $data->vals()), "\n"; } waitpid( $pid, 0 ); }

        OO interface with locking.

        use strict; use warnings; use MCE::Mutex; use MCE::Shared; my $mutex = MCE::Mutex->new(); my $data = MCE::Shared->array(); if ( ! defined ( my $pid = fork() ) ) { die "Cannot fork!: $!"; } elsif ( $pid == 0 ) { # Child MCE::Shared->init(); my $item; sleep 1; for ( my $i = 1; $i <= 10; $i++ ) { $mutex->lock(); $item = $data->shift(); $mutex->unlock(); $item = '' if ( ! defined $item ); print "Child: $item\n"; } } else { # Parent for ( my $i = 1; $i <= 10; $i++ ) { $mutex->lock(); $data->push($i); $mutex->unlock(); print "Parent: ", join(" ", $data->vals()), "\n"; } waitpid( $pid, 0 ); }

        OO interface (de-referencing) with locking.

        use strict; use warnings; use MCE::Shared; my $data = MCE::Shared->array(); if ( ! defined ( my $pid = fork() ) ) { die "Cannot fork!: $!"; } elsif ( $pid == 0 ) { # Child MCE::Shared->init(); my $item; sleep 1; for ( my $i = 1; $i <= 10; $i++ ) { $item = shift @$data; $item = '' if ( ! defined $item ); print "Child: $item\n"; } } else { # Parent for ( my $i = 1; $i <= 10; $i++ ) { push @$data, $i; print "Parent: @$data\n"; } waitpid( $pid, 0 ); }

        Summary.

        Locking may be omitted at the application level for the OO interface including de-referencing unless wanting to call multiple OO methods. Furthermore, MCE::Shared::{ Array, Hash, Ordhash, and Scalar } provide sugar methods resembling the Redis API without having to call set and get explicitly.

        # Locking is necessary via the TIE interface. # Below, set and get are called separately behind the scene ( 2 IPC st +atements ). my $m1 = MCE::Mutex->new(); tie my @a1, 'MCE::Shared'; $m1->lock; my $v0 = $a1[0] = "item0"; $m1->unlock; # Locking is optional at the application level when using the OO inter +face. # This is made possible by the single point of entry. my $a2 = MCE::Shared->array(); my $v2 = $a2->set(0, "item2"); my $a3 = MCE::Shared->array(); my $v3 = $a3->[0] = "item3";

        Regards, Mario.

        Hello Bloehdian,

        Re: I checked the documentation on CPAN, but it is not totally clear to me, how I would implement locking with this module.

        MCE::Shared 1.806, released today, contains a new section titled LOCKING in the documentation.

        Regards, Mario.

Re^2: IPC:Shareable: Not an array reference
by Marshall (Canon) on Oct 11, 2016 at 06:17 UTC

      Hello Marshall,

      Supporting the Windows platform took many attempts and am embarrassed to say how many hours spent just so that MCE::Shared works there too. What is cool about MCE::Shared is the ability to share data no matter how workers are spawned (e.g. via threads, via fork, MCE, MCE::Hobo, Parallel::ForkManager, and possibly other parallel modules).

      The MCE module supports Perl 5.8 and above whereas MCE::Shared requires Perl 5.10.1. This is the reason the two modules are separated. There are still users out there wanting MCE to run on Perl 5.8 minimally (e.g. App::Netdisco, Test::Perl::Critic).

      Regards, Mario.