http://www.perlmonks.org?node_id=288039


in reply to Things you need to know before programming Perl ithreads

liz++, thanks.

I got curious about how closures would do, so I tried your technique to see what happens (perl 5.8.1-RC2),

use threads(); { my $foo = 'bar'; sub foo () :lvalue { $foo } } threads->new( sub { foo = 'quux'; print 'thread: coderef = ', \&foo, $/; print 'foo is ', foo, $/; } )->join; print 'main: coderef = ', \&foo, $/; print 'foo is ', foo, $/; __END__ $ perl thrcl.pl thread: coderef = CODE(0x8129538) foo is quux main: coderef = CODE(0x8060674) foo is bar
so the object of the closure is duplicated - you did say everything, but I had to check.

Now, what if I want all threads to see the same thing?

use threads(); use threads::shared; { my $foo :shared = 'bar'; sub foo () :lvalue { $foo } } threads->new( sub { foo = 'quux'; print 'thread: coderef = ', \&foo, $/; print 'foo is ', foo, $/; } )->join; print 'main: coderef = ', \&foo, $/; print 'foo is ', foo, $/; __END__ $ perl thrcl.pl thread: coderef = CODE(0x8163924) foo is quux main: coderef = CODE(0x80fe448) foo is quux
so both foo()'s point to the same cloistered object.

Trying to stick the :shared attribute on sub foo did no good. I expected it to prevent duplication of the code, but perl smacked me down with,

$ perl thrcl.pl Invalid CODE attribute: shared at thrcl.pl line 5 BEGIN failed--compilation aborted at thrcl.pl line 5.
I suspect I need to read some more of the fine manual.

After Compline,
Zaxo

Replies are listed 'Best First'.
Re: Re: Things you need to know before programming Perl ithreads
by liz (Monsignor) on Aug 31, 2003 at 18:18 UTC
    ...so both foo()'s point to the same cloistered object.

    Nope. That's not really true. When you do use threads::shared(), what in fact happens is that in the background a thread is started. The dataspace of that hidden thread, contains the final version of each shared variable.

    All the other "shared" variables are basically simply tied variables to the "threads::shared::xxx" module. When want to get the value of a shared scalar, internally the FETCH subroutine gets excuted which fetches the value from the hidden thread, stores it in your local thread dataspace (mainly for consistency with XS modules) and returns that value. So the same value exists both in your thread as well as in the hidden thread.

    Same thing happens if you want to store a value in a shared scalar: the STORE subroutine stores the new value both in your local thread dataspace and in the hidden thread's dataspace. Some mutexing is involved of course. But e.g. incrementing a shared variable without locking is not guaranteed to increment. This is because incrementing with tied variables is implemented as a FETCH and a STORE, and between the FETCH and the STORE in one thread, another thread can already have done a FETCH. The following will rarely show the expected total:

    use threads (); use threads::shared (); my $scalar : shared = 0; my @thread; push( @thread,threads->new( sub { $scalar++ for 1..100000 } ) ) for 1. +.10; $_->join foreach @thread; # wait for all threads to finish print "scalar = $scalar\n";
    You'd expect $scalar to have the value of 10 * 100000 = 1000000, however unless you have a very fast machine, you will find it to be significantly less. To do this properly, you would have to lock the variable before doing the increment. For example:
    use threads (); use threads::shared (); my $scalar : shared = 0; my @thread; push( @thread,threads->new( sub { for (1..100000) { {lock $scalar; $scalar++} } } ) ) for 1..10; $_->join foreach @thread; # wait for all threads to finish print "scalar = $scalar\n";
    This should show the expected result: 1000000.

    Liz