箭头记号(取值)
$$rarray[1] 等价于 $rarray->[1]$$rhash{'a'} 等价于 $rhash->{'a'}
注意;这种记号仅对单键值的索引起作用.对于分段存取不行.
$rarrapackegey->[1,2]同$rarray->[2],Perl将括号中的内容视为逗号分隔的表达式,该表达式返回最后一个元素.
两个下标间的箭头可以省略.
$rarray->[1]->[1] 等价于 $rarray->[1][1]
匿名存储的引用布尔值
匿名存储即为不使用变量名动态为数据分配存储空间.
匿名存储的引用即为动态分配的存储空间的引用,指向这个内存地址.
$ra=[] #创建匿名数组,返回引用.
$rh={} #创建匿名散列表,返回引用.
$line = ['solid', 'black', ['1','2','3'] , ['4', '5', '6']];
$line->[0] = solid
共享与同步
threads::shared
和现有大多数线程模型不同,在 Perl ithreads 线程模型中,默认情况下任何数据结构都不是共享的。当一个新线程被创建以后,它就已经包含了当前所有数据结构的一份私有拷贝,新建线程中对这份拷贝的数据结构的任何操作都不会在其他线程中有效。因此,如果需要使用任何共享的数据,都必须显式地申明。threads::shared 包可以用来实现线程间共享数据的目的。
清单 9. 在线程中申明和使用共享数据
#!/usr/bin/perl
#
use threads;
use threads::shared;
use strict;
my $var :shared = 0; # use :share tag to define
my @array :shared = (); # use :share tag to define
my %hash = ();
share(%hash); # use share() funtion to define
sub start {
$var = 100;
@array[0] = 200;
@array[1] = 201;
$hash{'1'} = 301;
$hash{'2'} = 302;
}
sub verify {
sleep(1); # make sure thread t1 execute firstly
printf("var = $var\n"); # var=100
for(my $i = 0; $i < scalar(@array); $i++) {
printf("array[$i] = $array[$i]\n"); # array[0]=200; array[1]=201
}
foreach my $key ( sort( keys(%hash) ) ) {
printf("hash{$key} = $hash{$key}\n"); # hash{1}=301; hash{2}=302
}
}
my $t1 = threads->create( \&start );
my $t2 = threads->create( \&verify );
$t1->join();
$t2->join();
锁
多线程间既然有了共享的数据,那么就必须对共享数据进行小心地访问,否则,冲突在所难免。Perl ithreads 线程模型中内置的 lock 方法实现了线程间共享数据的锁机制。有趣的是,并不存在一个 unlock 方法用来显式地解锁,锁的生命周期以代码块为单位,也就是说,当 lock 操作所在的代码块执行结束之后,也就是锁被隐式释放之时。例如
清单 10.线程中的锁机制
use threads::shared;
# in thread 1
{
lock( $share ); # lock for 3 seconds
sleep(3); # other threads can not lock again
}
# unlock implicitly now after the block
# in thread 2
{
lock($share); # will be blocked, as already locked by thread 1
$share++; # after thread 1 quit from the block
}
# unlock implicitly now after the block
上面的示例中,我们在 thread 1 中使用 lock 方法锁住了一个普通的标量,这会导致 thread 2 在试图获取 $share 变量的锁时被阻塞,当 thread 1 从调用 lock 的代码块中退出时,锁被隐式地释放,从而 thread 2 阻塞结束,lock 成功以后,thread 2 才可以执行 $share++ 的操作。对于数组和哈希表来说,lock 必须用在整个数据结构上,而不是用在数组或哈希表的某一个元素上。例如
清单 11. 在数组或哈希表上使用锁机制
use threads;
use threads::shared;
{
lock(@share); # the array has been locked
lock(%hash); # the hash has been locked
sleep(3); # other threads can not lock again
}
{
lock($share[1]); # error will occur
lock($hash{key}); # error will occur
}
假如一个线程对某一个共享变量实施了锁操作,在它没有释放锁之前,如果另外一个线程也对这个共享变量实施锁操作,那么这个线程就会被阻塞,阻塞不会被自动中止而是直到前一个线程将锁释放为止。这样的模式就带来了我们常见的死锁问题。例如
清单 12. 线程中的死锁
use threads;
use threads::shared;
# in thread 1
{
lock($a); # lock for 3 seconds
sleep(3); # other threads can not lock again
lock($b); # dead lock here
}
# in thread 2
{
lock($b); # will be blocked, as already locked by thread 1
sleep(3); # after thread 1 quit from the block
lock($a); # dead lock here
}
死锁常常是多线程程序中最隐蔽的问题,往往难以发现与调试,也增加了排查问题的难度。为了避免在程序中死锁的问题,在程序中我们应该尽量避免同时获取多个共享变量的锁,如果无法避免,那么一是要尽量使用相同的顺序来获取多个共享变量的锁,另外也要尽可能地细化上锁的粒度,减少上锁的时间。
信号量
Thread::Semaphore 包为线程提供了信号量的支持。你可以创建一个自己的信号量,并通过 down 操作和 up 操作来实现对资源的同步访问。实际上,down 操作和 up 操作对应的就是我们所熟知的 P 操作和 V 操作。从内部实现上看,Thread::Semaphore 本质上就是加了锁的共享变量,无非是把这个加了锁的共享变量封装成了一个线程安全的包而已。由于信号量不必与任何变量绑定,因此,它非常灵活,可以用来控制你想同步的任何数据结构和程序行为。例如
清单 13. 线程中的信号量
use threads;
use threads::shared;
use Thread::Semaphore;
my $s = Thread::Semaphore->new();
$s->down(); # P operation
...
$s->up(); # V operation
从本质上说,信号量是一个共享的整型变量的引用。默认情况下,它的初始值为 1,down 操作使它的值减 1,up 操作使它的值加 1。当然,你也可以自定义信号量初始值和每次 up 或 down 操作时信号量的变化。例如
清单 14. 线程中的信号量
use threads;
use Thread::Semaphore;
my $s = Thread::Semaphore->new(5);
printf("s = " . ${$s} . "\n"); # s = 5
$s->down(3);
printf("s = " . ${$s} . "\n"); # s = 2
...
$s->up(4);
printf("s = " . ${$s} . "\n"); # s = 6
线程队列
Thread::Queue 包为线程提供了线程安全的队列支持。与信号量类似,从内部实现上看,Thread::Queue 也是把一个通过锁机制实现同步访问的共享队列封装成了一个线程安全的包,并提供统一的使用接口。Thread::Queue 在某些情况下可以大大简化线程间通信的难度和成本。例如在生产者 - 消费者模型中,生产者可以不断地在线程队列上做 enqueue 操作,而消费者只需要不断地在线程队列上做 dequeue 操作,这就很简单地实现了生产者和消费者之间同步的问题。例如
清单 15. 生产者 - 消费者模型中对线程队列的使用
#!/usr/bin/perl
use threads;
use Thread::Queue;
my $q = Thread::Queue->new();
sub produce {
my $name = shift;
while(1) {
my $r = int(rand(100));
$q->enqueue($r);
printf("$name produce $r\n");
sleep(int(rand(3)));
}
}
sub consume {
my $name = shift;
while(my $r = $q->dequeue()) {
printf("consume $r\n");
}
}
my $producer1 = threads->create(\&produce, "producer1");
my $producer2 = threads->create(\&produce, "producer2");
my $consumer1 = threads->create(\&consume, "consumer2");
$producer1->join();
$producer2->join();
$consumer1->join();
====以下是抄录别人的
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
2**3代表2的3次方
12,
13,
14,
15,
16,
17,
18,
$str=$str.” ”;等价于$str.=””;
19,
20,
21,
由于字符串’0’跟数字0是同一个标量值,所以perl会将它们一视同仁。也就是说,字符串’0’是唯一被当成“假”值的非空字符串。
22,
运用整行输入操作符<STDIN>,Perl以“标准输入”读进一行文字(读取到换行符为止)。
Perl程序会停下来,等待输入某些字符,直到换行符(按下Enter键)出现为止。
<STDIN>所返回的字符串值通常最后都会跟着一个换行符。
23,
它只能用在一种变量上,该变量的内容必须为字符串。如果此字符串的结尾是换行符,chomp就会将它移除。
Chomp($text=<STDIN>);#读入文字,略过最后的换行符
使用chomp时,可以加上括号,也可以不加。
字符串后面如果有两个以上的换行符,chomp只会删除一个。
24,
假如对索引值超过数组尾端的元素进行赋值,数组将会依需要自动扩大----只要有可用的内存配置给Perl,数组的长度是没有上限的。
对于正在使用的数组rocks而言,最后一个元素的索引值$#rocks,但$#rocks并不等于元素的个数,因为还多出一个偏号为0的元素。
从数组尾端往回计数的“负数数组索引值”。
25,
圆括号内一串以逗号分隔的值
(1,2,3)
(1..100)#100个整数所构成的列表
..为范围操作符。这个操作符会由左边的标量计数到右边,每次加1,以产生一连串的数值。
26.,当希望引用整个数组时,只要在数组名之前加上@符号,就可以立即使用整个数组。可以将@符号读作“全部的”。
27,pop和push操作符
Pop操作符可用来取出数组中最后一个元素并且将它返回。
Push会添加一个元素(或是一串元素)到数组的尾端push(@array,0)
Push的一个参数或pop的唯一参数必须是数组变量。
28,shift和unshift操作符
Shift是数组的“开头”删除相应的元素
Unshift是数组的“开头”添加相应的元素。
29,foreach 等价于c语言里面的for
控制变量并不是列表元素的复制品,事实上,它就是列表元素本身。也就是说,假如在循环中修改了控制变量,实际上会修改到相应元素值本身。
30.,reverse操作符
31,sort操作符
读取列表的值(可能来自数组),而且会根据字符 编码的顺序对他们进行排序。
32,“上下文”:表达式所出现的位置
在Perl中,表达式的返回值一定会符合他们的上下文。
以数组的“名称”为例:在列表上下文中,它会产生元素的列表
但是在标量上下文中,则会返回数组中元素的个数
@people=qw(fred,barney,betty);
@sort=sort @people;
$number=42+@people;
33,只要元素是赋值给列表的,不管元素的个数,它就是列表上下文。
强制指定标量上下文 scalar,它不是函数,只是告诉perl提供标量上下文。
34,子例程的定义可以放在程序中的任何地方,通常不需任何形式的前置声明。即子例程的定义具有全局性。
35,定义子例程
{….}
36,调用子例程
37,子例程的返回值
38,子例程的参数
Perl子例程可以有参数。要传递参数列表到子例程里,只要在子例程调用后面加上被括号括住的列表表达式就可以了。
如$n=&max(10,15);
Perl会自动将参数列表存储到名为@_的特殊数组里,在子例程执行期间内都有效。即第一个参数存储与$_[0],第二个参数存储于$_[1]。
假如已经有了全局变量@_,则该变量在子例程调用前会先被存起来,并在子例程返回时恢复原本的值。即,子例程可以将参数传给其他的子例程,而不用担心遗失自己的@_变量。
39,子例程里的私有变量
在Perl里,所有变量都被默认为全局变量。但可运用my 操作符来创建称为词法变量的私有变量。
sub max
{
}
40,更好的&max例程
{
}
}
$max_so_far;
}
41,my操作符并不会更改变量赋值时的上下文。
42,use strict编译命令
43,数组的索引$#
44,defined函数
45,标准输入(1)chomp($line=<STDIN>);
46,从钻石操作符输入
}
//默认的参数都是$_
47,调用参数
48,输出至标准输出
49,数组与printf
printf “The itemsare:\”.(s\n”x@items),@items;
在标量上下文中只用了一次@items以取得它的长度,然后又在列表上下文中用了一次@items以取得它的内容。
50,文件句柄
代表Perl进程与外界之间的输入/输出联系。
STDIN是标准输入流,STDOUT是标准输出流。
51,打开文件句柄
(1):打开(已存在)文件dino;
(2)利用shell重定向,将文件内容经STDIN读入程序里。
(3)创建一个新的文件,如果已经有一个名为Fred的文件,这么做会清楚原有的内容并且以新内容取代之。
(4)以添加的方式来打开文件,若不存在,会新建一个文件。
52,关闭文件句柄
当重新打开某个文件句柄时,Perl会自动关闭原来的文件句柄。
53,用die来处理严重错误
59,散列的赋值运算
%ip_address=reverse %host_name;
60,大箭号
61,散列函数
62,each函数
}
63,散列函数
64,正则表达式:是一个“匹配”或“不匹配”特定字符串的模板。
65,使用简易模式
66,关于元字符(是特殊符号)
67,简易的量词
68,模式组
69,择一匹配
70,字符集:方括号(或称中括号)里一连串可能的字符。它只会匹配单一字符,但是该字符可以是字符集里的任何一个。
71,字符集简写
(1)
(2)
单词则表示为” \w “
(3)
72,反义简写的排除
73,以/i来进行不区分大小写的模式匹配
74,以/s来匹配任意字符
75,用/x加上空白
76,组合选项修饰符
77,锚点
78,单词锚点
79,绑定操作符
80,匹配变量
81,记忆的持久性
这些“匹配变量”的内容通常会维持到下一次模式匹配时。也就是说,失败的匹配会保留前次的记忆内容,而成功地匹配会将它们重设。
82,通用量词
到目前为止,已经见过了3个量词:*、+和?。如果这3个量词都不符合需要,还可以在花括号里指定重现次数的范围。
如:/a{5,15}/
如果 省略第二个数字(但包含数字),则表示匹配次数没有上限。
如果同时省略逗号与上限次数,那么花括号里的数字就表示一个固定次数。
83,模式测试程序:测试正则表达式是否匹配成功
#!perl
While(<>){
Chomp;
If(/YOUR_PATTERN_GOES_HERE/){
Print“matched:|$`<$&>$’|\n”;
}else{
Print “no matched:|$_|\n”;
}
}
84,Perl的编译工具是ActivePerl。
85,以S/ / /进行替换
86,以/g进行全局替换
87,大小写转换
88,split操作符
89,join 函数
90,非贪心的量词
S#<BOLD>(.*)</BOLD>#$1#g;
若字符串为……with<BOLD> Fred</BOLD> and<BOLD> Wilma</BOLD>.
则去掉的是第一个<BOLD>和最后一个</BOLD>
所以应改为:
91,/m修饰符:多行的意思,可对每一个操作。
92,要在Perl中直接修改文件内容,可利用钻石操作符(<>)。
93,系统时间命令:date
94,对于钻石操作符的再理解:它会自动打开文件,而且如果没有指定文件,它就会从标准输入读进数据。此时S^I的默认值为undef。但若S^I中是个字符串,该字符串就会变成备份文件的扩展名。
如:S^I=”.bal”;
若S^I=”“;则不会备份。
95,只从命令行来修改文件:
96,不具有记忆功能的圆括号
97,unless控制结构
98,until控制结构
99,{…}:可以把某个变量放到一个未修饰块内并且在块内声明变量。
100,循环控制
(1)
(2)
(3)
(4)
101,在目录树中移动
102,glob操作符
103,目录句柄
104,移除文件
105,重命名文件
106,建立及移除目录
107,用index寻找子字符串
108,使用substr操作子字符串