线程是一个好东西,他不象进程占用那么多的内存,因不他不需要主空间,不需要进程控制块。他只共享所有主进程的所有内容。所以今天我们来研究一下线程的特点之一,共享的变量。
在线程中变量的基本使用的方法
如果你打算使用线程,常常会在多个子线程之间共享一些变量,常用的共享的变量可以是标量,数组,散列(hash)之类,好象 glob 和子程序不行,不过也没有必要了是吧。根据我的使用,我感觉是标量共享是直接使用,但哈希,数组只共享值的部分。
共享变量入门:
#!/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}++;
}
}其实还是上一个文章的例子,这个例子本来就是为这个文章设计的,上一个例子有关 Perl 的锁,其实没有必要写成这样。从上面我们可以见到,共享变量使用的时候 lock 一下,在建一个共享的线程之间的变量是,要使用 shared 来指定.
引用的线程共享
好,上面是使用的普通的变量的共享变量,但我们能使用引用和对象来共享吗?这是我们比较关心的。我们下面就来测试一下,引用在共享变量中是否能用.
use strict;
use threads;
use threads::shared;
my %hash : shared;
my $t = threads->create(\&test);
$t->join;
sub test{
$hash{test}{test}++;
}输出我们发现如下:
Thread 1 terminated abnormally: Invalid value for shared scalar at threads2.pl line 10.
我们发现,我们建了一个共享的变量,%hash 时,没有问题。但我们向 $hash{test} 中放一个匿名的哈希变量时,被 threads::shared 模块检查出来,讲这个不能放入,所以导致错误。因为匿名的哈希没有声明是不能共享的。
这是我们写 Perl 程序时常用的方法,不能共享引用,怎么办啦?
use strict;
use threads;
use threads::shared;
my %hash : shared;
$hash{test} = &share({});
my $t = threads->create(\&test);
$t->join;
sub test{
$hash{test}{test}++;
}在这,多了一个 $hash{test} = &share({}); 我们告诉线程,我们将要放入一个共享的匿名的哈希变量来做为引用。这时就可以使用引来了。所以在这种情况我们需要先声明匿名的哈希变量来做为引用。这时如果有多级的数据结构的引用也可以,使用相同的方法就行了,象 test 下面如果还有一个 try 的话,就使用 $hash{test}{try} = &share ({});来一级一级的声明。
如果我们是一个新的 hash 需要加引用,只有一级二级,还好办,要是我们的引用是多级,多级引用下面又有多线,我们就哭了,声明共享变量都需要写很多行,这时我们可以使用 threads::shared 提供给我们的 shared_clone 功能。
例如:
$VAR1 = {
' 103班' => {
'男' => [
'张三',
'李四',
'王五'
]
},
'101班' => {
'男' => [
'张三',
'李四',
'王五'
]
},
'100班' => {
'女' => [
'A',
'B'
],
'男' => [
'张三',
'李四',
'王五'
]
}
};象上面这种,要照层次来声明是很累的。还有这个数据结构本来就存要的话,在声明也很累。所以使用 shared_clone 可以很好的解决。
use strict;
use threads;
use threads::shared;
use Data::Dumper;
my $hash_ref = shared_clone( {
'100班' => {
'男' => ["张三","李四", "王五"],
'女' => ['A','B']
},
'101班' => {
'男' => ["张三","李四", "王五"],
},
' 103班' => {
'男' => ["张三","李四", "王五"],
}
});
my $t = threads->create(\&test);
$t->join;
sub test{
print Dumper $hash_ref;
}对象的线程共享
我们常常想在线程中使用同一个对象,多个线程,来操作同一个对象,这时线程中是怎么做啦?默认的对象是在线程中不共享的,也不能正常的使用的。
下面的方法,可以使用对象,在线程中,但注意,我们使用时也需要对对象进行 lock.不然会和上一个文章中讲的,线程中的锁中没锁一样,数据会乱掉.
#!/usr/bin/perl
use threads;
use threads::shared;
my $obj = &shared_clone (FOO->new); # 要给变量声明成共享
my $th1 = threads->create(\&test1);
my $th2 = threads->create(\&test2);
$th1->join;
$th2->join;
$obj->display;
sub test1 {
lock ($obj);
for( 1 .. 1000 ){
$obj->add_number();
}
}
sub test2 {
lock ($obj);
for( 1 .. 1000 ){
$obj->add_number();
}
}
package FOO;
sub new {
my $class = shift;
my $data = { 'number' => 0 };
my $self = bless ($data, $class);
return $self;
}
sub add_number {
my $self = shift;
$self->{number} ++;
}
sub display {
my $self = shift;
print $self->{number}."\n"
}
1;这其实一样是使用的 shared_clone 记的使用对象的时候,需要锁。