Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling

Tk with threads

by llancet (Friar)
on May 12, 2008 at 02:05 UTC ( #686022=perlquestion: print w/replies, xml ) Need Help??
llancet has asked for the wisdom of the Perl Monks concerning the following question:

I found a problem while using "Tk" with "threads" together, when it is going to collect a thread (no matter "join" or "detach", no matter the thread is created by "create()" or "async{}").
/usr/bin/perl use Tk; use threads; use strict; my $mw=MainWindow->new(-title=>'test'); my $button=$mw->Button( -text => 'show the problem', -command => sub{ print "button: ouch!\n"; my $thread=async{print "inside thread\n";}; print "before join\n"; $thread->join; } ); MainLoop;

Replies are listed 'Best First'.
Re: Tk with threads
by GrandFather (Sage) on May 12, 2008 at 02:32 UTC

    Tk and threads can be even more problematic than the usual range of problems with thread handling in Perl. The key is to create any threads you want to use before you call any Tk code and make sure you don't include Tk code in the threaded code.

    If those constraints sound bothersome then you may be able to solve the problem using POE and Tk, but a little Super Searching indicates that Tk and POE tend to fight somewhat too.

    Perl is environmentally friendly - it saves trees

      I can't see how POE and Tk are fighting - they cooperate. POE queues the main window's destroy event and passes control to Tk. POE code is called from Tk's event dispatcher; then POE uses Tk's event loop handler inside it's own event loop to serve Tk events from inside it's own event loop.

      But if by fighting you mean loading order you're right - POE has support for Tk, not the other way round, so you have to load POE first, and access the Main Window via $poe_main_window:

      use POE qw(Loop::Tk); POE::Session->create( inline_states => { _start => sub { $_[KERNEL]->yield("next") }, next => sub { print "tick...\n"; $_[KERNEL]->delay(next => 1); }, }, ); $button = $poe_main_window->Button( -text => 'Press me', -command => sub { exit; }, )->pack(); $poe_kernel->run();

      I haven't found a way (yet) to successfully load POE at some time within a running Tk application and shoehorn its event dispatcher somehow into the Tk event loop.


      _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                    /\_¯/(q    /
      ----------------------------  \__(m.====·.(_("always off the crowd"))."·
      ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Tk with threads
by renodino (Curate) on May 12, 2008 at 14:09 UTC
    Creating new threads inside dispatched closures from within Tk's internals is probably a very bad idea. Frankly, spawning a thread from *any* closure is probably a bad idea (threads already have a enough issues with non-determinism).

    I'd recommend creating a Thread::Queue, then creating a thread (or pool of threads) and using your Button dispatch to forward command/control msgs to the threads.

    Perl Contrarian & SQL fanboy
Re: Tk with threads
by zentara (Archbishop) on May 12, 2008 at 18:38 UTC
    With Tk, you MUST create the thread before any Tk objects are created, because duplicate copies of them will get cross-linked in the threads, and cause weird errors. Here is the simplest solution. Create your threads first, and put them in a sleep loop. Then build your Tk frontend and control the threads thru shared variables. Also, do not attempt to directly modify a Tk widget from another thread. Use shared variables to signal changes, then let the main Tk thread adjust the widgets. See Tk-with-worker-threads and PerlTk on a thread.... You can also share filehandles between threads, here is a simple example (without Tk, but you could display the output in a text widget if desired)
    #!/usr/bin/perl use warnings; use strict; use threads; use threads::shared; my %shash; #share(%shash); #will work only for first level keys my %hash; share ($shash{'go'}); share ($shash{'fileno'}); share ($shash{'pid'}); share ($shash{'die'}); $shash{'go'} = 0; $shash{'fileno'} = -1; $shash{'pid'} = -1; $shash{'die'} = 0; $hash{'thread'} = threads->new(\&work); $shash{'go'} = 1; sleep 1; # cheap hack to allow thread to setup my $fileno = $shash{'fileno'}; open (my $fh, "<&=$fileno") or warn "$!\n"; while ( <$fh> ){ print "In main-> $_"; } #wait for keypress to keep main thread alive <>; # control-c to exit ##################################### sub work{ $|++; while(1){ if($shash{'die'} == 1){ goto END }; if ( $shash{'go'} == 1 ){ my $pid = open(FH, "top -b |" ) or warn "$!\n"; my $fileno = fileno(FH); print "fileno->$fileno\n"; $shash{'fileno'} = $fileno; $shash{'go'} = 0; #turn off self before returning }else { sleep 1 } } END: } #####################################

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://686022]
Approved by GrandFather
[Eily]: I just wrote a oneliner to solve a (non perl) colleague's problem, and it worked on the first try
[Eily]: and he wasn't impressed
1nickt adds gingersnaps to the platter on the sideboard.

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (10)
As of 2017-04-26 15:00 GMT
Find Nodes?
    Voting Booth?
    I'm a fool:

    Results (482 votes). Check out past polls.