我曾经File::Find
遍历目录树和Win32::File
'sGetAttributes
函数来查看在其中找到的文件的属性。这在单线程程序中有效。
然后我将目录遍历移到一个单独的线程中,它停止工作。GetAttributes
对每个文件都失败,并在$^E
.
我将问题追溯到File::Find
使用的事实chdir
,并且显然GetAttributes
不使用当前目录。我可以通过将绝对路径传递给它来解决这个问题,但是我可能会遇到路径长度限制,并且在运行这个脚本的地方肯定会出现长路径,所以我真的需要利用chdir
相对路径。
为了演示这个问题,这里有一个脚本,它在当前目录中创建一个文件,在子目录中创建另一个文件,chdir's 到子目录,并以 3 种方式查找文件:system("dir")
、open
和GetAttributes
.
当脚本不带参数运行时,dir
显示子目录,在子目录中open
找到文件,并GetAttributes
成功返回其属性。当使用 运行时--thread
,所有测试都在子线程中完成,dir
并且open
仍然可以工作,但是GetAttributes
失败了。然后它调用GetAttributes
原始目录中的文件(我们已经从 chdir 中删除了)并找到了那个文件!不知何故GetAttributes
正在使用进程的原始工作目录 - 或者可能是主线程的工作目录 - 与所有其他文件操作不同。
我怎样才能解决这个问题?如果这很重要,我可以保证主线程不会做任何 chdir'ing。
use strict;
use warnings;
use threads;
use Data::Dumper;
use Win32::File qw/GetAttributes/;
sub doit
{
chdir("testdir") or die "chdir: $!\n";
system "dir";
my $attribs;
open F, '<', "file.txt" or die "open: $!\n";
print "open succeeded. File contents:\n-------\n", <F>, "\n--------\n";
close F;
my $x = GetAttributes("file.txt", $attribs);
print Dumper [$x, $attribs, $!, $^E];
if(!$x) {
# If we didn't find the file we were supposed to find, how about the
# bad one?
$x = GetAttributes("badfile.txt", $attribs);
if($x) {
print "GetAttributes found the bad file!\n";
if(open F, '<', "badfile.txt") {
print "opened the bad file\n";
close F;
} else {
print "But open didn't open it. Error: $! ($^E)\n";
}
}
}
}
# Setup
-d "testdir" or mkdir "testdir" or die "mkdir testdir: $!\n";
if(!-f "badfile.txt") {
open F, '>', "badfile.txt" or die "create badfile.txt: $!\n";
print F "bad\n";
close F;
}
if(!-f "testdir/file.txt") {
open F, '>', "testdir/file.txt" or die "create testdir/file.txt: $!\n";
print F "hello\n";
close F;
}
# Option 1: do it in the main thread - works fine
if(!(@ARGV && $ARGV[0] eq '--thread')) {
doit();
}
# Option 2: do it in a secondary thread - GetAttributes fails
if(@ARGV && $ARGV[0] eq '--thread') {
my $thr = threads->create(\&doit);
$thr->join();
}