如何共享包含文件句柄的对象?
Perl 线程不支持共享文件句柄。共享数据结构的所有元素都必须共享。如果需要共享包含文件句柄的对象,这就会出现问题。
{
package Foo;
use Mouse;
has fh =>
is => 'rw',
default => sub { \*STDOUT };
}
use threads;
use threads::shared;
my $obj = Foo->new;
$obj = shared_clone($obj); # error: "Unsupported ref type: GLOB"
print {$obj->fh} "Hello, world!\n";
文件句柄是否“共享”并不重要,它仅用于输出。也许有一个技巧可以将文件句柄存储在共享对象之外?
该对象实际上包含在另一个共享对象中,而另一个共享对象又在另一个共享对象中,依此类推。具有讽刺意味的是,所讨论的对象本身从不使用线程,但如果用户使用线程,则必须在整个进程中保持协调。
有问题的真实代码可以在这里看到:这些对象用于配置格式化输出的位置。对象是必要的,因为 输出并不总是发送到文件句柄。
Perl threads do not support sharing filehandles. All the elements of a shared data structure must be shared. This presents a problem if one needs to share an object which contains a filehandle.
{
package Foo;
use Mouse;
has fh =>
is => 'rw',
default => sub { \*STDOUT };
}
use threads;
use threads::shared;
my $obj = Foo->new;
$obj = shared_clone($obj); # error: "Unsupported ref type: GLOB"
print {$obj->fh} "Hello, world!\n";
It really doesn't matter if the filehandle is "shared" or not, it's only used for output. Perhaps there is a trick where the filehandle is stored outside the shared object?
This object is actually contained in another shared object which is in another and so on. The grand irony is the objects in question never use threads themselves, but must remain coordinated across the process if the user uses threads.
The real code in question can be seen here: These objects are used to configure where formatted output goes. An object is necessary because output does not always go to a filehandle.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我目前无法访问线程 Perl,因此不能保证这会起作用。
但是一种有点简单的方法是使用抽象级别并将键/索引存储到全局文件句柄哈希/数组中到对象中,类似于以下内容:
我有一种强烈的感觉,您还需要以某种方式线程-确保 ID 列表的安全,因此可能需要一个共享索引计数器,而不是
$stdout = scalar(@filehandles);
I don't have access to threaded Perl at the moment, so can't guarantee that this will work.
But a somewhat simplistic approach would be to use a level of abstraction and store a key/index into a global filehandle hash/array into the object, something similar to the following:
I have a strong feeling that you would need to somehow also thread-safe the list of IDs, so perhaps an shared index counter would be needed instead of
$stdout = scalar(@filehandles);
作为我使用全局数组的其他答案的替代方案,这是 Perlmonks 的另一种方法:
http://perlmonks.org/?node_id=395513
它的工作原理是实际存储
fileno
(文件描述符)文件句柄。以下是他的示例代码,基于 BrowserUk 发布的内容:注意 - 截至 2004 年,此代码存在一个错误,您无法从 >1 个线程的共享文件句柄中读取。我猜写作是可以的。有关如何在共享文件句柄上进行同步写入(来自同一 Monk)的更多具体信息:http://www.perlmonks.org/ ?node_id=807540
As an alternative to my other answer with global array, here's another approach from Perlmonks:
http://perlmonks.org/?node_id=395513
It works by actually storing
fileno
(file descriptor) of the filehandle. Here's his sample code based on what BrowserUk posted:CAVEAT - as of 2004, this had a bug where you couldn't read from the shared filehandle from >1 thread. I am guessing that writing is OK. More specifics on how to do synchronised writes on a shared filehandle (from the same Monk): http://www.perlmonks.org/?node_id=807540
我突然想到有两种可能的解决方案:
@DVK 的建议都是关于执行 1。
但是 2 在某些方面比 1 更简单。Formatter 可以保存 Streamer 对象的标识符,而不是保存 Streamer 对象本身。如果 Streamer 是由内向外实现的,那自然会发生!
不幸的是,引用地址在线程之间发生变化,即使是共享线程也是如此。这可以通过 Hash::Util::FieldHash 来解决,但这是5.10的事情,我必须支持5.8。可以使用 CLONE 将一些东西放在一起。
It just occurred to me there's two possible solutions:
@DVK's suggestions are all about doing 1.
But 2 is in some ways simpler than 1. Instead of holding the Streamer object itself, the Formatter can hold an identifier to the Streamer object. If the Streamer is implemented inside-out, that happens naturally!
Unfortunately, reference addresses change between threads, even shared ones. This can be solved with Hash::Util::FieldHash, but that's a 5.10 thing and I have to support 5.8. It's possible something could be put together using CLONE.
这就是我的结论...
使用类型强制可以确保即使在对象构造函数中也会发生从文件句柄到键的转换。
它可以工作,但有缺陷:
每个对象都冗余地存储其文件句柄。如果一堆对象都存储相同的文件句柄,它们可能只存储一次。诀窍是如何识别相同的文件句柄。
fileno
或 refaddr 是选项。删除对象时,文件句柄不会从 %Filehandle_Storage 中删除。我最初使用
DESTROY
方法来执行此操作,但由于对象克隆习惯用法是$clone = shared_clone($obj)
一旦 $obj 消失,$clone 的文件句柄就会被丢弃的范围。儿童发生的变化不会被分享。
对于我的目的来说,这些都是可以接受的,每个进程只会创建少数这些对象。
Here's what I wound up with...
Using type coercion ensures that the translation from filehandle to key happens even in the object constructor.
It works, but it has flaws:
Each object stores its filehandle redundantly. If a bunch of objects all store the same filehandle they could probably just store it once. The trick would be how to identify the same filehandle.
fileno
or the refaddr are options.The filehandle is not removed from %Filehandle_Storage upon object deletion. I originally put in a
DESTROY
method to do so, but since the object cloning idiom is$clone = shared_clone($obj)
$clone's filehandle is trashed once $obj goes out of scope.Changes which occur in children are not shared.
These are all acceptable for my purposes which will only create a handful of these objects per process.
话又说回来,如果没有的话,可以使用 https://metacpan.org/module/Coro对它的巨魔过敏反应。
Then again, one could use https://metacpan.org/module/Coro if one did not have an allergic reaction to its trolldocs.