问题的背景:
自 Perl 5.005 开始支持线程操作,但由于语言实现的特点,长时间保持实验性质,并伴随着大量的错误和限制。从 Perl 5.8 开始,threads(以及 threads::shared)模块变得足够稳定以处理严肃的任务,但 Perl 的线程模型与其他许多编程语言有很大不同:每个线程获得其所有变量的副本,只有通过 threads::shared 明确声明的结构才能共享访问。
问题:
由于写时复制(copy-on-write)的语义,“普通”变量在线程之间不可见。未使用 threads::shared 分配数据会导致状态不同步。在不正确使用锁的情况下,存在竞争、死锁或不一致更改的风险。
解决方案:
通过 use threads::shared 声明共享变量。使用 lock 阻止对共享数据的访问,特别是在多个线程同时读取/写入的情况下。使用 join/detach 方法管理线程的生命周期。对于复杂结构,单独声明每个共享元素,因为只有“顶层”结构不提供完全的线程安全性。
代码示例:
use threads; use threads::shared; my $counter :shared = 0; my @threads; for (1..10) { push @threads, threads->create(sub { for (1..1000) { lock($counter); ++$counter; } }); } $_->join() for @threads; print "Counter: $counter ";
关键特性:
:shared 是否在没有额外 lock 的情况下提供线程安全性?
不提供。:shared 属性允许在线程之间访问变量,但更改(例如,++ 或 --)并不是原子性的。每个临界区都需要 lock。
可以通过一个 :shared 指令在线程之间共享复杂结构(数组的哈希)吗?
不可以。只有数组或哈希的“顶层”会被共享。每个嵌套元素也应声明为共享,否则内部结构对其他线程不可见。
一个线程可以通过调用 exit 杀死另一个线程吗?
不可以。exit 完全终止进程,而不是单独的线程。要停止线程,可以在线程内使用 exit 或通过 join/detach 的逻辑管理。
两个线程同时在没有锁的情况下增加 $counter :shared。最终结果小于预期(典型的丢失更新问题)。
优点:
缺点:
在每次更改共享变量时实现锁。对于大的结构,逐元素嵌套锁。
优点:
缺点: