将子例程引用传递给perl线程

时间:2013-07-17 14:47:55

标签: multithreading perl reference subroutine

我正在尝试将子例程引用传递给perl线程来实现threadMap子例程。我处在一个我必须“自己动手”大多数事情的环境中;安装新的perl软件包不是一种选择。

另外,我正在运行perl版本5.10。在我的工作环境中,perl的版本> 5.10不可用。

我可以毫无困难地传递子程序引用。但是,一旦我尝试将子例程引用传递给线程,该线程似乎不理解它。

这是我提出的threadMap子程序,我相信,它的评论足以解释感兴趣的问题回答者。

#input: hash with keys L (listref), f (function which can apply to each element of L), and optionally nThreads
#default for nThreads is 50
#divides L into sublists for each thread, then kicks off threads
#each thread applies f to each element of the sublist
#and then returns the result of $f on each item
#output: map{ &$f($_) } @{$L}, but done threadily
sub threadMap{
  my %arg = @_;
  my ($L,$f,$nThr) = ($arg{L},$arg{f},$arg{nThreads});
  my $MAXTHREADS = 50;
  if(not defined $nThr or $nThr > $MAXTHREADS){
   $nThr = $MAXTHREADS;
  }

  &log(1,"threadMap: I have f $f");

  my @threadLists = &makeSublistsForThreads($L,$nThr);
  #in the event that L is less than $nThr, we reduce the number of threads
  $nThr = scalar(@threadLists);
  my @threads; 
  my @ret;
  for(0 .. $nThr-1){
  #invoke the threads in list context
  #    push @threads, threads->create({'context' => 'list'}, sub{ my ($L,$f) = @_; print "I have L <@$L> and f $f\n"; return (map{ &f($_) } @{$L}) }, ($threadLists[$_],$f) );
  push @threads, threads->create({'context' => 'list'}, sub{ my ($L) = @_; print "I have L <@$L> and f $f\n"; return (map{ &f($_) } @{$L}) }, ($threadLists[$_]) );
  }
  for(@threads){
  #each thread returns its items, so we get them back in order
      push @ret, $_->join();
  }
  return @ret;
}

当我运行一个名为'foo'的脚本时,它有大约以下内容:

my @L = (1 .. 5); 
my $f = sub{ my ($i) = @_; return 100*$i; };

print "I have f $f\n";

@out = &threadMap("L"=>\@L,"f"=>$f);
&log(1,"I had input <@L> and output <@out>");

my @realOut = map{ &$f($_) } @L; 
&log(1,"Output should be <@realOut>");

我得到了这个输出:

  

我有f CODE(0xbf3530)

     

Wed Jul 17 10:27:49 2013:threadMap:我有f CODE(0xbf3530)

     

我有L&lt; 1&gt;和f CODE(0x110f100)

     

线程1异常终止:未定义的子程序&amp; main :: f被调用   at / u / jamie /perl/jdPerlLib.pl第6037行。

     

我有L&lt; 2&gt;和f CODE(0x16b3df0)

     

线程2异常终止:未定义的子程序&amp; main :: f被调用   at / u / jamie /perl/jdPerlLib.pl第6037行。

     

我有L&lt; 3&gt;和f CODE(0x1a7d7b0)

     

线程3异常终止:未定义的子程序&amp; main :: f被调用   在/u/jamie/perl/jdPerlLib.pl第6037行。

     

我有L&lt; 4&gt;和f CODE(0x1fbb600)

     

线程4异常终止:未定义的子程序&amp; main :: f被调用   在/u/jamie/perl/jdPerlLib.pl第6037行。

     

我有L&lt; 5&gt;和f CODE(0x7fd5240b78c0)

     

线程5异常终止:未定义的子程序&amp; main :: f被调用   在/u/jamie/perl/jdPerlLib.pl第6037行。

     

2013年7月17日星期三10:27:49:我输入&lt; 1 2 3 4 5&gt;并输出&lt;&gt;

这告诉我函数引用从foo到我的threadMap子例程的顶部是常量,但是一旦它被传递给线程,它就会被改变并变得混乱。为什么是这样?我可以避免吗?

请注意,它同时失败

  #invoke the threads in list context
  #    push @threads, threads->create({'context' => 'list'}, sub{ my ($L,$f) = @_; print "I have L <@$L> and f $f\n"; return (map{ &f($_) } @{$L}) }, ($threadLists[$_],$f) );
  push @threads, threads->create({'context' => 'list'}, sub{ my ($L) = @_; print "I have L <@$L> and f $f\n"; return (map{ &f($_) } @{$L}) }, ($threadLists[$_]) );

  #invoke the threads in list context
      push @threads, threads->create({'context' => 'list'}, sub{ my ($L,$f) = @_; print "I have L <@$L> and f $f\n"; return (map{ &f($_) } @{$L}) }, ($threadLists[$_],$f) );
  #push @threads, threads->create({'context' => 'list'}, sub{ my ($L) = @_; print "I have L <@$L> and f $f\n"; return (map{ &f($_) } @{$L}) }, ($threadLists[$_]) );

另外,由于这是我第一篇关于stackoverflow的帖子,我的问题是否清晰,或者过于/不够冗长?

1 个答案:

答案 0 :(得分:3)

您正在调用threadMap中名为“f”的函数:

map{ &f($_) } ...

错误消息指示的哪个函数不存在。

您的意思是取消引用并调用 CODEref:

map { $f->($_) } ...

在您的更新评论中,代码有效,因为您确实定义了名为“f”的子。

顺便说一下,你通常应该用&符号来调用perl subs。这是来自perl 4的保留语法,并且在perl 5中具有非常特定的语义,当你只想调用sub时几乎不需要它。

调用&f将禁用原型处理并可以传递@_ - 如果该行为对您来说似乎没有用,那么请不要使用该功能。当表示子本身(例如,&my $coderef = \&f)和特殊defined &f调用时,也会使用goto &f&也取消引用CODE引用,通常是为了调用它,但是使用箭头和括号更明显地表达了该操作:$coderef->()