Perl中的多线程技术

3

请帮助我将我的脚本改为多线程。我已经阅读了threads::shared模块的文档,但是并没有帮助我理解如何实现。

    use threads;
use threads::shared;
use LWP::UserAgent;
use HTTP::Cookies;

my $NUM_WORKERS = 2;

sub worker {
   my ($i) = @_;
   my ($web, $ck) = browser();
    ($username, $password) = split ':', $acc;
    my $url = 'http://www.site.ru/?tkn'. int(rand(10000));
    my $response = $web->post($url, Content =>
                    [//////]);
    while(1)
    {
        my $url = 'http://www.site.ru/dk?st.page='.$i.'&st.name=%D0%B0';
        my $response  = $web->get($url);
        @list = ($response->content =~ /card_wrp"><div class="photoWrapper"><div><a href="\/(.*?)\?/g);
        @popl = ($response->content =~/<\/div><div class="info">(.*?)<\/div>/g);

        for ($j = 0; $j <= scalar @list - 1; $j++)
        {
            $popl[$j] =~ s/&nbsp;//g;
            open F, ">>gr.txt";
            print F $list[$j].':'.$popl[$j]. "\n";
            close F;
        }
        print "[+] Page $i \n";

    }
}

my $i :shared = 1;
my $last = 79265;
my @workers;
for (1..$NUM_WORKERS) {
   push @workers, async {
      while (1) {
         my $i;
         {
            lock $I;
            return if $i == $last;
            $i = ++$I;
         }
         worker($i);
      }
   };
}

$_->join() for @workers;
sub browser 
{
 my $web = new LWP::UserAgent;
 my $ck = new HTTP::Cookies;
    $web->cookie_jar($ck);
    $web->agent('Opera/9.80 (Windows 7; U; en) Presto/2.9.168 Version/11.50');
    $web->requests_redirectable(0); 

    $web->timeout(5);
 return $web, $ck;
}
sub loadf {
    open (F, "<".$_[0]) or erroropen($_[0]); 
    chomp(my @data = <F>);
    close F;
    return @data;
}

我不知道哪些变量需要共享。感谢所有愿意帮助我的成员。


我在你上一个问题的链接中提供的解决方案,在这里同样适用。 - ikegami
1个回答

8
如果没有线程,工作循环将会像这样:

for my $i (1..79265) {
   worker($i);
}

问题在于变量不可共享,for 保持着无法共享的内部状态,因此我们需要将其改写为没有这些问题的内容。
选项1:
my @a = 1..79265;
while (@a) {
   worker(shift(@a));
}

选项2:
my $i = 0;
while (++$i <= 79265) {
   worker($i);
}

要并行化任一版本,只需确保在检查和使用@a/$i之间,其值不会改变。这可以通过添加锁来实现。

选项1:

my @a :shared = 1..79265;
while (1) {
   my $i;
   { lock @a; return if !@a; $i = shift(@a); }
   worker($i);
}

方案二:

my $I :shared = 1;
while (1) {
   my $i;
   { lock $I; $i = $I; return if ++$I > 79265; }
   worker($i);
}

选项1是下面介绍的Thread::Queue解决方案的基础(但它也可以直接使用)。选项2在下面直接使用。


我通常会使用Thread::QueueThread::Queue::Any

use threads;
use Thread::Queue qw( );

my $NUM_WORKERS = 5;

sub worker {
   my ($i) = @_;
   ... put your download code here ...
}

my $q = Thread::Queue->new();
my @workers;
for (1..$NUM_WORKERS) {
   push @workers, async {
      while (defined(my $i = $q->dequeue())) {
         worker($i);
      }
   };
}

$q->enqueue($_) for 1..79265;
$q->enqueue(undef) for @workers;
$_->join() for @workers;

但是在这里我们可以轻松地不用它:

use threads;
use threads::shared;

my $NUM_WORKERS = 5;

sub worker {
   my ($i) = @_;
   ... put your download code here ...
}

my $I :shared = 1;
my $last = 79265;
my @workers;
for (1..$NUM_WORKERS) {
   push @workers, async {
      while (1) {
         my $i;
         {
            lock $I;
            $i = $I;
            return if ++$I > $last;
         }
         worker($i);
      }
   };
}

$_->join() for @workers;

@user1614240,已添加解释。 - ikegami
谢谢,我重写了代码,但它不起作用,请在主题中查看。 - user1614240
1
@user1614240,它为什么不工作?我测试了我的代码。修复了一个跳过1的错误。我怀疑你的新问题与你最初的问题没有任何关系。请发布一个具有问题的最小化可运行演示的新问题。 - ikegami

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接