Multithreading Perl-同步数据访问

Multithreading Perl-同步数据访问,multithreading,perl,Multithreading,Perl,我开始学习并行编程,我想比较单线程程序和多线程程序 我需要做一个非常简单的算法,在一分钟内计算出可能的最大素数,并显示最后计算出的素数及其在素数中的位置 例如,素数23将显示为数字23及其位置9,因为它是第9个素数 在不使用线程的情况下,找到的素数为233596,最后一个素数为3246107。但是对于线程,发现了229972个素数,最后一个素数是3192463 我认为这是错误的,因为多线程应该比单线程获得更好的结果。我相信这是一个非常基本的错误,但我无法解决它,因为我仍然不太了解Perl的并行性

我开始学习并行编程,我想比较单线程程序和多线程程序

我需要做一个非常简单的算法,在一分钟内计算出可能的最大素数,并显示最后计算出的素数及其在素数中的位置

例如,素数23将显示为数字23及其位置9,因为它是第9个素数

在不使用线程的情况下,找到的素数为233596,最后一个素数为3246107。但是对于线程,发现了229972个素数,最后一个素数是3192463

我认为这是错误的,因为多线程应该比单线程获得更好的结果。我相信这是一个非常基本的错误,但我无法解决它,因为我仍然不太了解Perl的并行性

这是代码。它计算一分钟单线程的素数,然后使用共享变量对四个线程进行相同的计算

use threads;
use threads::shared;

my $seconds = 60;

 # WITHOUT THREAD #
print "\n\n Calc without Threads:\n";
my $endTime = time() + $seconds;
calcWithoutThread();


print "\n\n ----------------===========================---------------- \n";

 # WITH THREAD #
print "\n\n Calc with Threads:\n";
my $prime :shared = 5;          # Starts from the 5th prime
my $totalPrime :shared = 2; # Starts with prime 2 and prime 3
my $lastPrime :shared = 0;
my $endTime1 = time() + $seconds;
my $thread1 = threads->create(\&calcWithThread);
my $thread2 = threads->create(\&calcWithThread);
my $thread3 = threads->create(\&calcWithThread);
my $thread4 = threads->create(\&calcWithThread);
$thread1->join();
$thread2->join();
$thread3->join();
$thread4->join();
print " Was found $totalPrime prime numbers. Last prime: $lastPrime.";


# SUB's #

sub calcWithoutThread{
    $prime = 5;            # Starts from the 5th prime
    $totalPrime = 2;           # Starts with prime 2 and prime 3
    $lastPrime = 0;
    while (time() < $endTime){
        if(calcPrime($prime)){  
            $totalPrime ++;
            $lastPrime = $prime;
        }
        $prime ++;
    }
    print " Was found $totalPrime prime numbers. Last prime: $lastPrime.";
}

sub calcWithThread{
    while (time() < $endTime1) {
        lock($prime);           
        if(calcPrime($prime)){
            $totalPrime ++;
            $lastPrime = $prime;
        }
        $prime ++;
    }   
}

sub calcPrime{
    for($i=2 ; $i< sqrt ($prime) ; $i++){
        if( $prime % $i == 0){
            return 0;
        }
    }
    return 1;   
}
使用线程;
使用线程::共享;
我的$seconds=60;
#无螺纹#
打印“\n\n不带线程的计算:\n”;
my$endTime=time()+$seconds;
calcWithoutThread();
打印“\n\n----------------------------------------------============================================================--------------------\n”;
#用线#
打印“\n\n使用线程计算:\n”;
我的$prime:shared=5;#从第五个素数开始
我的$totalPrime:shared=2;#从素数2和素数3开始
我的$lastPrime:shared=0;
my$endTime1=时间()+$s;
my$thread1=线程->创建(\&calcWithThread);
my$thread2=线程->创建(\&calcWithThread);
my$thread3=线程->创建(\&calcWithThread);
my$thread4=线程->创建(\&calcWithThread);
$thread1->join();
$thread2->join();
$thread3->join();
$thread4->join();
print“已找到$totalPrime素数。最后一个素数:$lastPrime。”;
#潜艇#
无螺纹接头{
$prime=5;#从第5个prime开始
$totalPrime=2;#以素数2和素数3开头
$lastPrime=0;
while(time()<$endTime){
if(calcPrime($prime)){
$totalPrime++;
$lastPrime=$prime;
}
$prime++;
}
print“已找到$totalPrime素数。最后一个素数:$lastPrime。”;
}
子计算线程{
while(time()<$endTime1){
锁($prime);
if(calcPrime($prime)){
$totalPrime++;
$lastPrime=$prime;
}
$prime++;
}   
}
次计算时间{
对于($i=2;$i

逻辑是,线程同步执行此计算(无论是否为素数),并且在计算时也不会重叠值。

问题在于,您的线程通过锁定变量
$prime
彼此同步。这意味着它们没有机会同时运行,并且还将承受切换线程和同步访问变量的开销

素数对于并行处理来说不是一个很好的测试,因为每个素数的计算都取决于前面的结果。但是,您可以通过保留一个单独的
$prime
变量来实现这一点,但是对于四个线程,从5、6、7和8开始,并在测试之间添加4。这样它们就不会重复彼此的功,而是一起覆盖每个整数

这马上就有一个问题,偶数中没有一个是素数,所以四个线程中的两个永远不会产生结果。这仍然是对并行性的有效测试,但显然效率很低。您可以通过以5、7、9和11开始线程,并在每次测试之前递增8来修复此问题。那么每一条线索都将有利可图

不要忘记,您必须为单线程代码编写相同的算法,否则并行部分会获得不公平的优势


更新 也许更好的方法是锁定
$prime
只获取下一个要测试的数字并将其递增。这样,所有线程都可以并行地进行计算,并且只排队等待另一个作业

然后,您必须锁定
$total_prime
,以防止两个线程同时递增它,并且在该锁定生效时更新
$last_prime
。因为并行线程很可能会不按顺序生成素数,所以您还必须检查刚刚找到的素数是否大于任何线程发现的最新素数

您的子例程如下所示。我很抱歉更改了标识符,但是Perl传统上使用
snake\u case
,我觉得
camelCase
读起来很不舒服。对于许多母语不是英语的人来说,蛇形格也容易得多,因为他们不能如此容易地辨认出大写字母

sub calc_with_thread {

    while ( time() < $end_time_1 ) {

        my $test = do {
            lock $prime;
            $prime++;
        };

        if ( calc_prime($test) ) {
            lock $total_prime;
            ++$total_prime;
            $last_prime = $test if $test > $last_prime;
        }
    }   
}
带螺纹的子计算{
while(time()<$end\u time\u 1){
我的$test=do{
锁定$prime;
$prime++;
};
如果(计算素数($test)){
锁定$total_prime;
++$total_prime;
$last\u prime=$test如果$test>$last\u prime;
}
}   
}

多线程不是一种自动的速度提升

共享变量是绑定的,而绑定的变量速度较慢

这里有一个更简单、更完善的版本,它可以达到最大值,而不是按时间。它避免了锁定。线程的速度仍然明显较慢,因为每个线程都必须对其检查的每个数字执行get和set操作

#!/usr/bin/env perl

use strict;
use warnings;
use Time::HiRes qw(time);
use v5.10;

use threads;
use threads::shared;

my $Max_Number = 50_000;

{
    print "\n\nCalc without Threads:\n";
    my $start_time = time;
    calcWithoutThread();
    my $end_time = time;
    say "$Max_Number took @{[ $end_time - $start_time ]} seconds.";
}

my $Shared_Prime :shared = 5;
{
    print "\n\nCalc with Threads:\n";
    my $start_time = time;
    my $thread1 = threads->create(\&calcWithThread);
    my $thread2 = threads->create(\&calcWithThread);
    my $thread3 = threads->create(\&calcWithThread);
    my $thread4 = threads->create(\&calcWithThread);
    $thread1->join();
    $thread2->join();
    $thread3->join();
    $thread4->join();
    my $end_time = time;
    say "$Max_Number took @{[ $end_time - $start_time ]} seconds.";
}

sub calcWithoutThread {
    my $prime = 5;                # Starts from the 5th prime

    while( $prime <= $Max_Number ) {
        calcPrime($prime++);
    }
}

sub calcWithThread {
    while( $Shared_Prime <= $Max_Number ) {
        calcPrime($Shared_Prime++);
    }
}

sub calcPrime {
    my $prime = shift;

    for( my $i=2 ; $i < sqrt($prime) ; $i++){
        if( $prime % $i == 0){
            return 0;
        }
    }

    return 1;   
}
#/usr/bin/env perl
严格使用;
使用警告;
使用时间:雇佣qw(时间);
使用v5.10;
使用线程;
使用线程::共享;
我的$Max_编号=50_000;
{
打印“\n\n不带线程的LC:\n”;
我的$start_time=时间;
calcWithoutThread();
我的$end_time=时间;
说“$Max_Number花费了@{[$end_time-$start_time]}秒。”;
}
我的$Shared_Prime:Shared=5;
{
普林
#!/usr/bin/env perl

use strict;
use warnings;
use Time::HiRes qw(time);
use v5.10;

use threads;

my $Max_Number = 7_000;

{
    print "\n\nCalc without Threads:\n";
    my $start_time = time;
    calcWithoutThread(5, 1);
    my $end_time = time;
    say "$Max_Number took @{[ $end_time - $start_time ]} seconds.";
}

{
    print "\n\nCalc with Threads:\n";
    my $start_time = time;
    my $thread1 = threads->create(\&calcWithThread, 5, 4 );
    my $thread2 = threads->create(\&calcWithThread, 6, 4 );
    my $thread3 = threads->create(\&calcWithThread, 7, 4 );
    my $thread4 = threads->create(\&calcWithThread, 8, 4 );
    $thread1->join();
    $thread2->join();
    $thread3->join();
    $thread4->join();
    my $end_time = time;
    say "$Max_Number took @{[ $end_time - $start_time ]} seconds.";
}

sub calcWithoutThread {
    my($start, $inc) = @_;

    my @primes;
    for( my $prime = $start; $prime <= $Max_Number; $prime += $inc ) {
        push @primes, $prime if calcPrime($prime);
    }

    return \@primes;
}

sub calcWithThread {
    my($start, $inc) = @_;

    my @primes;
    for( my $prime = $start; $prime <= $Max_Number; $prime += $inc ) {
        push @primes, $prime if calcPrime($prime);
    }

    return \@primes;
}

sub calcPrime {
    my $prime = shift;

    for( my $i=2 ; $i < sqrt($prime) ; $i++){
        if( $prime % $i == 0){
            return 0;
        }
    }

    return 1;   
}