in reply to Re^4: ithreads memory leak
in thread ithreads memory leak
I said 3 suggestion, but one of them was too complicated to merit discussion.
- So first the "right way" to do it.
Your walktimer thread sub becomes something like this:
sub walktimer { ## The queue handle is passed in when the thread is started my $Q = shift; #Sleep for 180 seconds, upon completion execute SQL to change OK_O +PEN back to all zero for $showcasenum ## A hash to remember when things time out. my %when; ## wake up once per second while( sleep 1 ) { ## while there are showcase numbers on the queue while( $Q->pending ) { ## grab the one at a time my $showcasenum = $Q->dequeue(); ## Calculate when the timeout should occur, ## and add the showcase number to the hash to be dealt wit +h at that time push @{ $when{ time() + $walktime } }, $showcasenumber; + } ## find any times that have expired (earlier than now!) for my $time ( grep{ $_ < time() } keys %when ) { ## And for each showcase number scheduled at that time for my showcasenum ( @{ $when{ $time } } ) { ## Do your db stuff my $dbh = DBI->connect('dbi:mysql:alarm:databasehost', +'username','password') or die "Connection Error: $DBI::errstr\n"; my $sql = "update Showcases set OK_OPEN=0 WHERE SC_NUM + = ?"; my $sth = $dbh->prepare($sql) or die "Can't prepare $s +ql: $dbh->errstr\n"; $sth->execute($showcasenum) or die "SQL Error: $DBI::e +rrstr\n"; $dbh->disconnect; } ## and remove the expired time from the hash delete $when{ $time }; } } }I hope the comments make it clear, but basically, instead of starting a new thread for each timeout, you start one thread and pass it a handle to a queue.
Then when you would have started a new walktimer thread, you simply post the showcase number to that queue. Ie this:
elsif( $okopen == 1) { #Kill the walk timer if it is running, someone opened +the showcase legit debug("Killing walking timer and starting open timer, +authorized user opened showcase $showcasenum"); if( $walkthread[$showcasenum] && $walkthread[$showcase +num]->is_running() && !$walkthread[$showcasenum]->is_detached) { $walkthread[$showcasenum]->kill('KILL')->detach(); } $openthread[$showcasenum] = threads->create(\&opentime +r, $showcasenum, $opentime, $showcasedesc); }becomes this:
elsif( $okopen == 1) { $Qwalktimer->enqueue( $showcasenum ); }You do the same thing for your opentimer sub thread.
Now, instead of starting a new thread each time(r:), and having to track them, and kill them if they still exist; before starting a new one; you:
- Create 2 queues:
One for opentimers and one for walktimers.
- Create (and detach) two threads at the start of the program.
Fire and forget. They'll go away when your main thread does.
- You send (queue) instructions to them and they take care of it at the appropriate time.
Two, long running threads that take instructions and do what's required. No memory growth.
- Create 2 queues:
- Now, the "quick fix option". Modify your walktimer this way:sub walktimer { #Sleep for 180 seconds, upon completion execute SQL to change OK_O +PEN back to all zero for $showcasenum my $showcasenum = $_[0]; debug("Launched walking timer thread for $showcasenum"); $SIG{'KILL'} = sub { threads->exit(); }; my $count = $walktime; sleep( 1 ) while --$count; ### Wake once per second for $walktime +seconds to allow the "signal handler" to respond to any pending "sign +als". if( threads->is_detached()) { threads->exit(); } my $dbh = DBI->connect('dbi:mysql:alarm:databasehost','username',' +password') or die "Connection Error: $DBI::errstr\n"; my $sql = "update Showcases set OK_OPEN=0 WHERE SC_NUM = ?"; my $sth = $dbh->prepare($sql) or die "Can't prepare $sql: $dbh->er +rstr\n"; $sth->execute($showcasenum) or die "SQL Error: $DBI::errstr\n"; $dbh->disconnect; debug("Walking thread for $showcasenum is expiring..."); threads->detach(); }
Ditto for opentimer(). And that should allow your current architecture to work. (I don't like it but ... its your code :)
If you choose option 1 and need further help, yell and I'll try to modify a copy of your code and post it, but I'm not going to waste the effort if you choose option 2 :)