Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot

Trouble passing an array reference to my threads

by dh (Novice)
on Nov 23, 2011 at 04:46 UTC ( #939592=perlquestion: print w/ replies, xml ) Need Help??
dh has asked for the wisdom of the Perl Monks concerning the following question:

Basically, I have an array called @host_array that contains information about the hosts that I want to see whether they are alive or dead. I need to check their current status against their old status and send an alert if something changes. Unfortunately, I can't seem to get the reference right. That means that when I update a value in the $task variable within sub 'pinger', it doesn't actually update the original array; just its local version of the data.

I want to pass a reference to the entire array to run_threaded and then a reference to a slice of that array from run_threaded to pinger.

populateHosts(1); while (1) { run_threaded(10,\&pinger,\@host_array); } sub run_threaded { my ($threads,$func_to_thread, $hosts) = @_; my (%threads); my $n = 0; my $debug = 1; my $m = $threads-1 > $#{$hosts} ? $#{$hosts} : $threads-1; while (1) { last if $n >= $#{$hosts}+1; foreach my $host (@$hosts[$n..$m]) { #Still a reference here! I can change the value of $host[ +x] and it will stick. $threads{$host} = new threads $func_to_thread, $host; } map { $threads{$_}->join if $threads{$_}; } @{$hosts}[$n..$m]; # work out the next range of instances to work on $n = $m == 0 ? 1 : $m+1; $m = $n+$threads-1 < $#{$hosts} ? $n+$threads-1 : $#{$hosts}; } } sub pinger{ $task = shift; # Create pinger $p = Net::Ping->new("tcp"); $p->port_number($task->[4]); if( $p->ping($task->[3], 6) ) { print " PASS: @$task[2] "; $task->[5] = 1; #I want to update @host_array, but it's not a reference! } else { print " FAIL: @$task[2] "; $task->[5] = 0; #I want to update @host_array, but it's not a reference! } $task->[2] .= "-2"; $p->close(); } sub populateHosts { ($init) = @_; # DEFINE A MySQL QUERY $query = "SELECT * FROM `sites`"; # EXECUTE THE QUERY $db->query($query); my $set = $db->create_record_iterator; $i=0; while (my $rec = $set->each) { $host_array[$i][0] = $i; $host_array[$i][1] = $rec->[0]; $host_array[$i][2] = $rec->[1]; $host_array[$i][3] = $rec->[2]; $host_array[$i][4] = $rec->[3]; if ($init) { $host_array[$i][5] = $rec->[4]; $host_array[$i][8] = $rec->[5]; $host_array[$i][6] = 1; $host_array[$i][7] = time; } $i++; } }

There are some lines that I omitted for brevity, but the function should still be similar. Thanks!

By the way, the great thread function 'run_threaded' came from

Comment on Trouble passing an array reference to my threads
Download Code
Re: Trouble passing an array reference to my threads
by NetWallah (Abbot) on Nov 23, 2011 at 06:39 UTC
    The thread inherits a COW (Copy on write) copy of a part of the array, so updates to that will not affect the original.

    What you need to do is to RETURN a value from your "sub pinger". This value can be collected when the thread is join()ed.
    Use the returned value (Which can be an array ref, in fact, you can return $task) to update the array.

    FWIW, I find the code style you used frightening, but am resisting the urge to comment on that. Should you choose to approach that subject, please request comment. I'm sure monks would be more than happy to share coding tips.

                "XML is like violence: if it doesn't solve your problem, use more."

      The thread inherits a COW (Copy on write) copy of a part of the array

      There's no COW, it's just a copy created the same time the thread created.

        You are probably right - I made some assumptions based on my (limited) understanding of how threads are implemented - I thought I read somewhere that there is an underlying fork, which does COW.

                    "XML is like violence: if it doesn't solve your problem, use more."

      I don't want to frighten anyone with my code. This code was pulled out of the full script, so it may be more frightening in fragment than its original.

      Some of the mess in the code, like not returning from the threads, comes from the fact that this code wasn't threaded until I realized there may be too many hosts to iterate quickly enough. Originally, it just passed from sub to sub for each host in step. I am going to try returning that value from a join tonight to keep the reference inside the thread caller.

      I would absolutely like to hear your comments on how the style and methodology may be improved. This code will be running for a while, so I want it to run well.

      Thank you!

Re: Trouble passing an array reference to my threads
by zentara (Archbishop) on Nov 23, 2011 at 10:49 UTC
    Instead of passing the array to the thread, you can just make @host_array shared, then the thread can modify it directly. A cheap example:
    #!/usr/bin/perl use warnings; use strict; use threads; use threads::shared; $|=1; #turn off buffering my @hosts_shared : shared; #declare as shared before setting value @hosts_shared = (1,2,3,4,5); print "@hosts_shared\n"; my $thread = threads->new(sub { print "Run the thread! array should be modified\n"; #modify shared array push @hosts_shared, 42; })->detach(); sleep 1; # cheap hack to allow thread time to work print "@hosts_shared\n"; print "Hit enter to exit\n"; <>;

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

Log In?

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

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (5)
As of 2014-09-20 15:38 GMT
Find Nodes?
    Voting Booth?

    How do you remember the number of days in each month?

    Results (160 votes), past polls