线程 - 在 Perl 中操作线程(仅适用于旧代码)
Thread
模块是旧式线程模型(称为5005threads)的前端,该模型在 5.005 版本中引入。该模型已弃用,并在 5.10 版本中删除。
为了旧代码和过渡期的向后兼容性,Thread
模块已被重新设计为新解释器线程(ithreads)模型的前端。但是,一些以前的功能不可用。此外,两种线程模型之间的数据共享模型完全不同,任何与数据共享相关的内容都需要以不同的方式考虑。使用ithreads,您必须显式地share()
线程之间的变量。
强烈建议您尽快将任何现有的线程代码迁移到新模型(即,使用threads
和threads::shared
模块)。
在 Perl 5.005 中,线程模型是所有数据都隐式共享,并且对数据的共享访问必须显式同步。这种模型被称为 5005threads。
在 Perl 5.6 中,引入了一种新模型,其中所有内容都是线程本地,并且对数据的共享访问必须显式声明。这种模型被称为 ithreads,代表“解释器线程”。
在 Perl 5.6 中,ithreads 模型不可用作公共 API;它仅作为内部 API 可供扩展编写者使用,以及在 Win32 平台上实现 fork() 模拟。
在 Perl 5.8 中,ithreads 模型通过 threads
模块变得可用,并且 5005threads 模型被弃用。
在 Perl 5.10 中,5005threads 模型从 Perl 解释器中移除。
use Thread qw(:DEFAULT async yield);
my $t = Thread->new(\&start_sub, @start_args);
$result = $t->join;
$t->detach;
if ($t->done) {
$t->join;
}
if($t->equal($another_thread)) {
# ...
}
yield();
my $tid = Thread->self->tid;
lock($scalar);
lock(@array);
lock(%hash);
my @list = Thread->list;
Thread
模块为 Perl 提供多线程支持。
new
在引用的子例程中启动一个新的执行线程。可选列表作为参数传递给子例程。执行在子例程和 new
调用后的代码中继续。
Thread->new
返回一个线程对象,表示新创建的线程。
lock
对变量加锁,直到锁超出作用域。
如果变量被另一个线程锁定,lock
调用将阻塞,直到它可用。lock
是递归的,因此对 lock
的多次调用是安全的——变量将保持锁定,直到对变量的最外层锁定超出作用域。
对变量的锁定只影响 lock
调用——它们不影响对变量的正常访问。(对子例程的锁定不同,稍后会介绍。)如果你真的,真的想要锁定来阻止访问,那么继续将它们绑定到某物并自己管理。这是有意为之。虽然管理对变量的访问是一件好事,但 Perl 不会强迫你离开它的客厅……
如果一个容器对象(如哈希或数组)被锁定,则该容器的所有元素都不会被锁定。例如,如果一个线程执行 lock @a
,任何其他线程执行 lock($a[12])
不会阻塞。
最后,lock
将精确地向上遍历引用 一层。lock(\$a)
等效于 lock($a)
,而 lock(\\$a)
则不是。
async
创建一个线程来立即执行紧随其后的代码块。此代码块被视为匿名子程序,因此在闭合大括号后必须有分号。与 Thread->new
一样,async
返回一个线程对象。
Thread->self
函数返回一个线程对象,该对象代表执行 Thread->self
调用的线程。
返回所有未加入、未分离的线程对象的列表。
cond_wait
函数接受一个 **已锁定** 的变量作为参数,解锁该变量,并阻塞直到另一个线程对同一个已锁定变量执行 cond_signal
或 cond_broadcast
操作。cond_wait
阻塞的变量在 cond_wait
满足后重新锁定。如果有多个线程在同一个变量上 cond_wait
,除了一个线程外,其他线程都会重新阻塞,等待重新获取该变量的锁。(因此,如果您只使用 cond_wait
进行同步,请尽快放弃锁。)
cond_signal
函数接受一个已锁定变量作为参数,并解除阻塞一个在该变量上 cond_wait
的线程。如果有多个线程在该变量上 cond_wait
阻塞,只有一个线程(哪个线程是不确定的)会被解除阻塞。
如果没有线程在该变量上 cond_wait
阻塞,则信号会被丢弃。
cond_broadcast
函数的工作原理类似于 cond_signal
。但是,cond_broadcast
会解除阻塞 **所有** 在已锁定变量上 cond_wait
阻塞的线程,而不是只解除阻塞一个线程。
yield
函数允许另一个线程接管 CPU 的控制权。确切的结果取决于实现。
join
等待一个线程结束并返回线程退出时产生的任何值。join
会阻塞直到线程结束,但如果线程已经终止,它不会阻塞。
如果被 join
的线程 die
了,它死亡时产生的错误将在此时返回。如果你不希望执行 join
的线程也死亡,你应该将 join
包裹在一个 eval
中,或者使用 eval
线程方法而不是 join
。
detach
告诉一个线程它永远不会被 join
,也就是说,一旦它停止运行,所有关于它的存在痕迹都可以被移除。分离线程中的错误将不会在任何地方可见 - 如果你想捕获它们,你应该使用 $SIG{__DIE__} 或类似的东西。
equal
测试两个线程对象是否代表同一个线程,如果它们相同则返回 true。
tid
方法返回线程的 tid。tid 是一个单调递增的整数,在创建线程时分配。程序的主线程的 tid 为零,而后续线程的 tid 从一开始分配。
done
方法返回 true 如果您正在检查的线程已完成,否则返回 false。
以下是在 5005threads 中实现的,但在 ithreads 中不再可用。
在 5005threads 中,您还可以 lock
一个子程序,这样从另一个线程调用该子程序的任何调用都会阻塞,直到锁被释放。
此外,子程序可以用 :locked
属性声明,这将序列化对子程序的访问,但允许不同的线程非同时访问。
eval
方法将一个 eval
包裹在一个 join
中,因此它等待一个线程退出,传递线程可能返回的任何值,并将任何错误放入 $@
中。
flags
方法返回线程的标志 - 一个整数,对应于线程的内部标志。