Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

About self-invoked threads into class

by Omniperl (Initiate)
on Jun 01, 2012 at 12:49 UTC ( #973751=perlquestion: print w/ replies, xml ) Need Help??
Omniperl has asked for the wisdom of the Perl Monks concerning the following question:

Venerable monks, I'm here because I need your ancient knowledge referred to classes and threads and how they are interrelated.

I have one small package named HardThread.pm, and a tiny perl script that uses the package.

package HardThread; use strict; use warnings; use threads; use threads::shared; use vars qw( $internal ); ## Variable declaration for internal functions my ( $thread ); sub new { my $class = shift; my $inter : shared = shared_clone({}); $inter->{INT1} = "internal 1"; $inter->{INT2} = "internal 2"; $inter->{INT3} = "internal 5"; return bless( \%{$inter}, $class ); } sub stop_thread { my $inter = shift; $inter->{THR}->kill('TERM'); $inter->{THR}->join(); } sub starter { my $inter = shift; $inter->{THR} = ref threads->create(sub{$thread->(\$inter->{INT1}) +}); return ( 1 ); } sub set_internal { my $inter = shift; if( @_ ) { my ( $given_key, $given_value ) = @_ if @_; $inter->{$given_key} = $given_value; return ( 1 ); } return ( 0 ); } sub printer { my $inter = shift; foreach my $key ( keys %{$inter} ) { print $key, " - ", $inter->{$key}, "\n"; } } $thread = sub { local *internal = $_[0]; my $control = 1; $SIG{'TERM'} = sub { $control = 0; }; while( $control == 1 ) { print "Thread shows internal value: ", $internal, "\n"; sleep 2; } return ( 1 ); }; 1;

The script is:

#! /usr/bin/perl use strict; use warnings; use HardThread; my $var = HardThread->new(); $var->printer(); $var->starter(); while( <STDIN> ) { chomp $_; $var->set_internal("INT1", $_); } $var->stop_thread(); sleep 5;

And what is the target with all this?

Basically, I want to define a class named HardThread.pm that self-invokes a thread and offers public functions to manage it.
However, I want to make thread able to access to an internal variable stored in class in order to make visible for the thread any future changes made on this variable.

Code listed above does it very well, and running the script generates the desired output, but function stop_thread cannot execute functions $inter->{THR}->kill('TERM') and $inter->{THR}->join() and hence, the script cannot wait to thread termination. What is wrong?

Waiting for your wisdom, I send you best regards.

PD: Apologize if I don't write English very well. I'm not an English-speaker and I have some difficulties to write it. Thanks a lot! :)

Comment on About self-invoked threads into class
Select or Download Code
Re: About self-invoked threads into class
by Eliya (Vicar) on Jun 01, 2012 at 13:58 UTC
    but function stop_thread cannot execute functions $inter->{THR}->kill('TERM') and ...

    Your immediate problem is that you're trying to call the kill method on the value

    ref threads->create(...)

    which is the string "threads", stored in $inter->{THR}, instead of the thread itself, i.e. what threads->create(...) returns.  However, when trying to fix the problem by getting rid of the "ref"

    $inter->{THR} = threads->create(sub{$thread->(\$inter->{INT1})});

    you'd get an "Invalid value for shared scalar" error...

    So, you probably want to take the thread variable out of the shared data structure, e.g. modify your code like this

    package HardThread; use strict; use warnings; use threads; use threads::shared; use vars qw( $internal ); ## Variable declaration for internal functions my ( $thread ); sub new { my $class = shift; my $inter : shared = shared_clone({}); $inter->{INT1} = "internal 1"; $inter->{INT2} = "internal 2"; $inter->{INT3} = "internal 5"; my $self = {}; $self->{inter} = $inter; return bless $self, $class; } sub stop_thread { my $self = shift; $self->{THR}->kill('TERM'); $self->{THR}->join(); } sub starter { my $self = shift; my $inter = $self->{inter}; $self->{THR} = threads->create(sub{$thread->(\$inter->{INT1})}); return ( 1 ); } sub set_internal { my $self = shift; my $inter = $self->{inter}; if( @_ ) { my ( $given_key, $given_value ) = @_ if @_; $inter->{$given_key} = $given_value; return ( 1 ); } return ( 0 ); } sub printer { my $self = shift; my $inter = $self->{inter}; foreach my $key ( keys %{$inter} ) { print $key, " - ", $inter->{$key}, "\n"; } } $thread = sub { local *internal = $_[0]; my $control = 1; $SIG{'TERM'} = sub { $control = 0; }; while( $control == 1 ) { print "Thread shows internal value: ", $internal, "\n"; sleep 2; } return ( 1 ); }; 1;

    The idea is to have a "wrapper" hash/object, which stores

    $self->{inter} # access to shared data $self->{THR} # access to thread

    With these modifications, the code works fine for me (if I'm understanding your intentions correctly).

Re: About self-invoked threads into class
by BrowserUk (Pope) on Jun 01, 2012 at 14:10 UTC
    ... function stop_thread cannot execute functions $inter->{THR}->kill('TERM') and $inter->{THR}->join() and hence, the script cannot wait to thread termination. What is wrong?

    Technically speaking, the reason that this line:

    $inter->{THR}->kill('SIGTERM');

    doesn't work, is because of what you are doing in this line:

    $inter->{THR} = ref threads->create(sub{$thread->(\$inter->{INT1}) +});

    That is, instead of storing the thread handle into $inter->{THR}, you are storing the return value of ref when applied to that thread handle. And that means you are storing:

    $t = async{ sleep 1000 };; print ref $t;; threads

    Ie. You are storing the text string 'threads'.

    Which means that when you come to try and call the instance method kill(), instead of passing an instance handle you are calling it as a class method, which (with warnings enabled), should have told you:

    Usage: $thr->kill('SIG...') at HardThread.pm line 23.

    It doesn't work because it doesn't know which thread to kill.

    To avoid that problem, you would need to store the thread handle directly thus:

    $inter->{THR} = threads->create(sub{$thread->(\$inter->{INT1})});

    Which you've probably tried, only to receive the fatal error message:

    Invalid value for shared scalar at HardThread.pm line 29.

    The way to avoid that is to use the shared_clone() function exported by threads::shared, to shared the thread object handle, thus:

    $inter->{THR} = shared_clone( threads->create(sub{$thread->(\$inte +r->{INT1})}) );

    Make that change, and your code will "work".


    That deals with the technical issues with your code; however, it doesn't deal with what (IMO) is a more important issue.

    That issue is that the design of your module using shared objects and (pseudo-)signals, in conjunction with threads is a fundamentally flawed concept.

    The explanation of why is difficult.

    • Every method call involves at least 2 context switches.

      Which make for very slow, computationally expensive objects.

    • Methods can be called concurrently from different threads, which means you need to add locking and synchronisation to every method.

      That makes the object slower still.

      Makes them complex to write, test and maintain.

    • Makes your threads run in 'lock-step'.
      • Thread 1 does some stuff, the calls a method on a shared object. It now has to block to wait for the method to return.
      • A context switch (eventually) occurs,
      • Thread 2, the 'object thread', which has been spinning its heals (polling) waiting for something to do, executes the (usually trivial) method code and returns the value; and goes back to polling.
      • A context switch occurs.
      • Thread 1 gets the return value from the method.

      It completely defeats the primary benefit of using threading; that of allowing the threads to do work simultaneously.

      Where is the benfit of using an 'object thread', if every time the caller thread calls a method, it has to block until the object thread gets swapped in, does it thing and returns the result?

    It is quite likely that this explanation will not convince you -- if you have used Java, the above scenario will likely seem like normal operating procedure -- but if what I've said makes sense to you, and you would like a better approach to solving your problem, then you'll need to describe that actual problem, instead of how you are currently trying to tackle it.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

      ... It is quite likely that this explanation will not convince you ...

      Such a thorough and well-reasoned explanation certainly should convince anyone.   Upvoted.

      “Threads” are all-too-often seen as being equivalent to “a unit of work,” when in fact they should be seen as a “worker.”   (The difference between someone who’s cooking a hamburger, and the hamburger itself.)   The diff between success and failure, between something that is efficient and something that can be considerably slower than “no threads at all,” is the workflow structure that surrounds those workers.   (That’s what Ray Kroc saw in the MacDonald brothers’ hamburger stand:   a better process.)   The OP’s design as it stands now, even if made to “work,” won’t hold a candle to what it properly should.

        Such a thorough and well-reasoned explanation certainly should convince anyone.

        Maybe, (and thanks), but the reality is that for many people who have come from Java and other multi-processing backgrounds, my explanation won't necessarily ring true, because it goes against much of what they were taught in colleges; or read in (often highly recommended) books (see the real time section:barriers; latches; exchangers) and on training courses.

        There has been -- and still is in many circles -- far too much emphasis placed on synchronisation -- which creates all the bad guys of multiprograminig as listed under "Realtime Anti-Patterns". If you don't synchronise at all, none of the bad guys can happen.

        (The difference between someone who’s cooking a hamburger, and the hamburger itself.)

        I don't think your burger analogy helps much here. The problem lies not (so much) in work-unit .v. worker, but in how the workers communicate.

        The best analogy I have is the difference between old-school voice mail; and those dratted call-center "Your message is in a queue and will be answered shortly - tick-tock -- We're sorry but due to the high volume of calls there are no free agents; please hold" queuing systems.

        The problem is not the queue per se, but the synchronicity of that queue from the callers perspective. From the call center's perspective, it is an efficient way of distributing the workload. But from the caller's perspective, having to stand around listening to the typical "Your call is important to us..." lies is just dead, wasted cycles.

        On the other hand, voice mail allows me to ask my question and get on with something else until an agent becomes free and calls me back. Ie. It is a (pair of) asynchronous queues.

        Of course, the downside of that is the phone tag game. It can happen in bi-directional queuing also.

        The solution is promises (also known as futures; also known as deferred synchronicity).


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        The start of some sanity?

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://973751]
Approved by Eliya
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (16)
As of 2014-07-22 15:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (117 votes), past polls