本来这个文章是要写 Perl 线程中的锁和共享变量的,这个是几个月前的计划,但一直没有时间,好不容易现在有点时间了,就准备了一下,但发现写一个Perl 中的锁就有非常多的内容了,所以就不写共享变量了,就下一个文章中写吧。
目前在 Linux 中线程和进程和最大分别,可能就是有共享变量就个东西了。其它的地方使用起来感觉不大,只是线程更加高效,占用更加少,但是也更加容易出问题。下面就看看 Perl 中的锁和相关的问题。
先看例子。
#!/usr/bin/perl
use strict;
use threads;
use threads::shared;
use Data::Dumper;
my $val : shared; # 共享变量
my %hash : shared; # 共享数组
my @array : shared; # 共享哈希
my $t1 = threads->create(\&test1);
my $t2 = threads->create(\&test2);
$t1->join; # 回收 t1 的线程
$t2->join;
print Dumper(\$val);
print Dumper(\@array);
print Dumper(\%hash);
sub test1 {
lock ($val); lock (@array); lock (%hash);
for ( 1 .. 1000 ){
$val++;
$array[0]++;
$hash{test}++;
}
}
sub test2 {
lock ($val); lock (@array); lock (%hash);
for ( 1 .. 1000 ){
$val++;
$array[0]++;
$hash{test}++;
}
}输出的结果
$VAR1 = \'2000';
$VAR1 = [
'2000'
];
$VAR1 = {
'test' => '2000'
};从上面,我们可以看出,在使用共享变量是,我们一定要使用一个 lock 来锁定当前需要共享的变量,这需要一个排它性。不然很容易出问题,不锁的话,会出什么问题啦,我们来看下面的例子,出掉锁以后会怎么样。
#!/usr/bin/perl
use strict;
use threads;
use threads::shared;
use Data::Dumper;
my $val : shared; # 共享变量
my %hash : shared; # 共享数组
my @array : shared; # 共享哈希
my $t1 = threads->create(\&test1);
my $t2 = threads->create(\&test2);
$t1->join; # 回收 t1 的线程
$t2->join;
print Dumper(\$val);
print Dumper(\@array);
print Dumper(\%hash);
sub test1 {
for ( 1 .. 1000 ){
$val++;
$array[0]++;
$hash{test}++;
}
}
sub test2 {
for ( 1 .. 1000 ){
$val++;
$array[0]++;
$hash{test}++;
}
}输出的结果很神奇,象这个样子
$VAR1 = \'1830';
$VAR1 = [
'1922'
];
$VAR1 = {
'test' => '1922'
};本来正常的结果是 2000 的,现在好了,都是乱的,主要是因为,当进程 test1 在使用共享变量时, test2 也同时拿到这个变量,本来每个加一起,合起来是 2000,但因为拿到相同的数字相加,所以结果就乱了。所以在使用线程时,一定要非常注意共享变量加锁。
在 Perl 中有个子函数的 lock ,也有对象的方法锁,使用的方法是
子函数的锁
sub frob : locked {
# 和以前一样
}
对象方法的锁
sub frob : locked method {
# 和以前一样
}
注意,上面的方法现在 Perl 中不可用了。但书中因为历史的原因,还写着这样的方法,我们可以使用下面的方法来测试,就知道结果了
#!/usr/bin/perl
use strict;
use threads;
use threads::shared;
use Data::Dumper;
my $val : shared; # 共享变量
my %hash : shared; # 共享数组
my @array : shared; # 共享哈希
my $t1 = threads->create(\&test1);
my $t2 = threads->create(\&test2);
$t1->join; # 回收 t1 的线程
$t2->join;
print Dumper(\$val);
print Dumper(\@array);
print Dumper(\%hash);
sub test1 : locked {
for ( 1 .. 1000 ){
$val++;
$array[0]++;
$hash{test}++;
}
}
sub test2 : locked {
for ( 1 .. 1000 ){
$val++;
$array[0]++;
$hash{test}++;
}
}这样输出的结果,会和没有加锁时会是一样的。