mahis_431 has asked for the wisdom of the Perl Monks concerning the following question:

At present I am working on GUI in Perl/Tk. In this GUI I created some image icon Buttons. And I want to make this buttons as Multi-Threaded. For one of the button's subroutine I created a thread. I created thread with all the rules ( I shared the variables that need work with thread, created thread at begininng). But I am facing a problem in getting this thread worked. The problem in the threaded subroutine is I am loading a external perl file( for example - do 'add.pl') which is not working ( i mean not loading). The external file also loads other files, modules in itself. What is the problem I am facing here? Is that I have make all the variables in this external file as shared variables for thread? The part of the code is given below:
use threads; use threads::shared; my $KillThread : shared; my $FunctionName : shared; my $ThreadWork : shared; my @ArgumentsThread : shared; my $RefResultFunction : shared; $ThreadWork = 0; $KillThread = 0; my %FunctionsToLaunchThread = ( "run_script" =>\&run_script ); # Creation of thread my $Thread = threads->create( \&WorkThread ); sub load { ......some code...... $run_button->configure(-command =>[ (\&runtk, $paste_text)],-state => +'normal'); ......some code.... } sub runtk { my $temp = $_[0], $FunctionName = "run_script"; @ArgumentsThread = ($temp); $ThreadWork = 1; $run_button->configure(-state => "disabled"); while ( $ThreadWork == 1 ) { sleep 0.2; $f->update; } $run_button->configure(-state => "normal"); } sub run_script{ open (MYFILE1, "subroutines.txt"); my @sub_lines = <MYFILE1>; my $current = localtime(time); #print "Current date and time is $current"; use POSIX qw(strftime); my $now_string = strftime "%d-%b-%Y--%T", localtime; print $now_string; my $str ="$now_string.txt"; print $str; open FILE, "+>>Test_Results/$str" or die $!; do "add.pl"; use Device::Modem::GSM; my $modem = new Device::Modem::GSM( port => "/dev/$port_global",log = +> "file,$now_string.log", loglevel => +'debug', # default is 'warning' + ); sub WorkThread { while (1) { if ( $ThreadWork == 1 ) { $FunctionsToLaunchThread{$FunctionName}->@ArgumentsThread); $ThreadWork = 0; } last if ( $KillThread == 1 ); sleep 0.5; } return; }

Replies are listed 'Best First'.
Re: Perl/Tk Multithreading
by zentara (Cardinal) on Jul 17, 2011 at 12:31 UTC
    Hi, you don't give a complete code script that we can actually run and test, so it's hard to say what is happening. The line in your thread
    $FunctionsToLaunchThread{$FunctionName}->@ArgumentsThread);
    isn't clear. What do you expect that to do?

    Also, you may have seen that while(1) thread code construct, from early examples of running threads from Tk, but now with later versions of Threads you can use signals to kill threads. See using the thread->kill() feature on linux

    Finally, since you are using some network code, you may be getting blocking on i/o, see Threads, bash, and networking

    It would be useful for you to create as simple of a running script as possible, which demonstrates your problem.


    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
      sorry I missed the opening parentheses for @Arguments first I declared a hash where functions are stored which I call them in thread, like: %FunctionsToLaunchThread = ( "run_script" => \&run_script ); and then I created a thread: $Thread = threads->create( \&WorkThread ); Now I created a subroutine sub runtk where i assigned
      $FunctionName = "run_script"; @ArgumentsThread = ($new);
      so now the following $FunctionsToLaunchThread{$FunctionName}->(@ArgumentsThread); becomes $FunctionsToLaunchThread{run_script}->($new) which is \&run_script->($new); The above function is expected to call the function in the thread. The run_script subroutine I already shared in previous post. As you said its true I am facing a blocking problem on the device I/O. The thread is created and is loading the do "add.pl" but now in the add.pl I am using network modules, this modules are blocked for the thread to work completely. I am getting following errors: 1. "unable call method purge_all() in the Modem.pm on undentified variable" 2. "undefined value for a shared variable at line.... in Modem.pm" So is there any way to work around this. I wanted to share the working code but it is too big.
        I am getting following errors: 1. "unable call method purge_all() in the Modem.pm on undentified variable" 2. "undefined value for a shared variable at line.... in Modem.pm" So is there any way to work around this.

        You probably should work on getting the network code functioning on it's own, before putting it into a thread. Some tricks are to wrap the network code in an eval with an alarm. See Thread Safe alarms?.

        You are not showing a complete code for this, so as a guess, one of the other mistakes may be that you are declaring your GSM network module globally in the main thread.... try to confine all network code, including the use statement, to the worker thread. Many modules are still not thread safe.


        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku ................... flash japh
      I will see if i can post a simple working code....actually the real working code is very big and other external files are loaded in mainscript. This external files are using network modules. This is the point where i am getting troubles.
Re: Perl/Tk Multithreading
by BrowserUk (Patriarch) on Jul 17, 2011 at 13:50 UTC
    ( for example - do 'add.pl') which is not working ( i mean not loading).

    How do you know it is not loading?

      Without thread created on the concerned subroutine(run_script)that worked, but when I created thread on subroutine it is failing to do so which made me concluded it is not loading. Anyway now it is loading, but I am facing modules interface problem in the thread because the loaded file(add.pl) has different modules in it( example I am using Device::Modem::GSM module in add.pl). So when the thread is going into the modem file it is not able to call certain methods. It is giving error to call the methods on execution. And as you can see Device::Modem::GSM is built on other modules like Device::Modem, Device::Serialport. So the problem is getting complicated. If I make any changes in the Modem modules my application will not be portable on machines. Any solutions please.............
        Any solutions please.............

        On the basis of the description so far. No.

        Will adding more words help. No.


        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.
Re: Perl/Tk Multithreading
by zentara (Cardinal) on Jul 18, 2011 at 12:10 UTC
    use Device::Modem::GSM) Because of these modules the thread is ending abnormally. This where I am facing the present problem

    It's probably because you are setting it up in a non-threadsafe manner. Googling for Device::Modem::GSM indicates it is threadsafe.

    Try to confine ALL code related to the GSM module to the thread. Don't try to pass subs from module to the thread.

    Also remember that threads:shared variables are not tied across thread boundaries. Each thread actively has to read the thread:shared variable for it to be updated in it's own space. This can be done easily in Tk with some timers, that do nothing but refresh-read all the shared variables at a 10 ms interval. Without seeing the actual code, all we can do is guess at where you are making the mistake.

    You might also want to ditch threads entirely and go for a forked solution.


    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
      Thank you for your kind suggestions. All your guess is truly my problem.

      1. I tried to confine ALL code of GSM module to the thread

      2. I am getting the following error:

      "Thread 1 terminated abnormally: Can't call method "purge_all" on an undefined value at widget.pl line 4198."

      3. The subroutine in which the "purge_all" appears is given below:

      (Note: this method is take from Module Device::Modem so i did not code this subroutine. Only thing I am passing arguments to this subroutine from my program).For complete code this module please see CPAN

      sub atsend { my ( $me, $msg ) = @_; my $cnt = 0; print "\nThe port \$me in keypress function is $me\n"; # Write message on port $me->port->purge_all; $cnt = $me->port->write($msg); my $lbuf=length($msg); my $ret; while ($cnt < $lbuf) { $ret = $me->port->write(substr($msg, $cnt)); $me->write_drain(); last unless defined $ret; $cnt += $ret; } $me->log->write('debug', 'atsend: wrote '.$cnt.'/'.length($msg).' +chars'); # If wrote all chars of `msg', we are successful return $cnt == length $msg; }

      4. In the above subroutine the error is exactly coming at the line:  $me->port->purge_all;

      5. The subroutine which calls the above module subroutine from my program is given below:

      sub keypress { my ($port_no,$key) = @_; $port_no =~ m/(\d+)/g; print "\nThe port \$port_no in keypress function is $port_no\n"; my $ms=$1; use Device::Modem::GSM; my $mod= new Device::Modem::GSM(port => "/dev/$port_no", log=> 'file,logfile.log', loglevel => 'info' # default is 'war +ning' ); $modem[$ms]=$mod; $key =~ s/^\s+//;# to remove leading whitespace $key =~ s/\s+$//;# to remove trailing whitespace my $st = "AT+CKPD="."\"$key\"".",1,1" ; $modem[$ms]-> atsend($st . Device::Modem::CR); my ($ok, $response) = $modem[$ms]->parse_answer($Device::Modem::STD_RE +SPONSE); return "$ok"; }

      6.Additional Information:

      a)The value of arguments passed to the module subroutine atsend() from my program subroutine keypress()are:

      AT+CKPD="m",1,1

      b)The value of the arguments received in the atsend() subroutine for variable $me is:

      Device::Modem::GSM=HASH(0xaece500)

      Note: This values I got from compiling.

      So whats the problem here? why it is not able to call method in Modem.pm module?

      My program owrks like gem when same arguments are passed without creating a thread.

        So whats the problem here? why it is not able to call method in Modem.pm module? My program works like gem when same arguments are passed without creating a thread.

        I tried to follow your description of the problem, but it still isn't clear that you are confining all of your network code to the worker thread as you seem to be creating the Device::Modem::GSM object in your keypress subroutine, which I assume is in your main thread.

        The problem does seem familiar to me though, as I had to work thru similar issues with various threaded programs. The general safe rule is only communicate with the thread thru shared variables, or options passed to the thread during thread->create. That means if you receive a keypress in your main thread, transfer it to a shared variable designed to hold it, then somehow get the thread to read the information from the shared variable, and do what needs to be done.

        So your use Device::Modem::GSM; statement, as well as my $mod= new Device::Modem::GSM() statement, should be confined to the thread, and your keypresses should be passed in thru a shared variable.

        If you havn't figured it out yet, by seeing it mentioned in numerous posts in the archives, but Perl threads are created by a copy-on-create method, which makes an exact copy of the parent thread at the time of creation. So, when you create a Device::Modem::GSM object in the main thread, after the worker thread gets created, the worker thread has to use try and access the object in the main thread, crossing a safety barrier, and it is the source of all sorts of errors like you are getting. Or, if you create the Device::Modem::GSM() object before the thread is created, there are essentially 2 objects, 1 in main, and a copy in the thread... that causes boundary errors too.

        I know you probably have invested quite a bit of time into your Tk frontend to this, but you may want to switch to Gtk2, because it has improved thread safety features, and can use subs which are common amoung threads, thru it's Glib::Idle->add method.

        But if you stick with Tk, make sure ALL network code is confined to the worker thread, and only communicate with the thread thru pre-defined shared variables. That means in your worker thread, you will need a loop of some sort to pick up the keypresses which your main thread has put in a shared variable. Don't try to pass the sub created in main, with options to the thread.


        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku ................... flash japh