【问题标题】:Is it safe to abort this file-searching thread?中止这个文件搜索线程是否安全?
【发布时间】:2023-03-24 22:19:01
【问题描述】:

一、代码:

lblFileNbr.Text = "?/?";
lblFileNbr.ToolTipText = "Searching for files...";
lock(_fileLock)
{
    _dirFiles = new string[0];
    _fileIndex = 0;
}
if(_fileThread != null && _fileThread.IsAlive)
{
    _fileThread.Abort();
}
_fileThread = new Thread(() =>
    {
        string dir = Path.GetDirectoryName(fileName) ?? ".";
        lock (_fileLock)
        {
            _dirFiles = GetImageFileExtensions().SelectMany(f => Directory.GetFiles(dir, f, _searchOption)).OrderBy(f => f).ToArray();
            _fileIndex = Array.IndexOf(_dirFiles, fileName);
        }
        int totalFileCount = Directory.GetFiles(dir, "*.*", _searchOption).Length;

        Invoke((MethodInvoker)delegate
        {
            lblFileNbr.Text = string.Format("{0}/{1}", NumberFormat(_fileIndex + 1), NumberFormat(_dirFiles.Length));
            lblFileNbr.ToolTipText = string.Format("{0} ({1} files ignored)", dir, NumberFormat(totalFileCount - _dirFiles.Length));
        });
    });
_fileThread.Start();

我正在构建一个小的图像查看程序。打开图像时,它会列出同一目录中的文件数。我注意到,当我在包含许多其他文件(比如 150K)的目录中打开图像时,构建文件列表需要几秒钟的时间。因此,我将此任务委托给另一个线程。

但是,如果您在完成搜索文件之前打开另一个图像,则旧计数不再相关,因此我将中止线程。

我正在锁定 _dirFiles 和 _fileIndex 因为我想添加一些 Left 和 Right 键功能来在照片之间切换,所以我必须访问其他地方(但在 UI 线程中)。

这样安全吗?现在C#中处理线程的方法似乎有几十种,我只是想要一些简单的东西。


fileName 是一个局部变量(这意味着它将被“复制”到匿名函数中,对吗?),而_searchOption 是只读的,所以我想这两个是可以安全访问的。

【问题讨论】:

    标签: winforms multithreading thread-safety thread-abort


    【解决方案1】:

    > 中止这个文件搜索线程是否安全?

    简短的回答是不!

    中止线程几乎是不安全的,当您可能正在执行本机代码时,此建议更适用。

    如果您不能以足够快的速度合作退出(因为您对Directory.GetFiles 的调用需要时间),您最好的选择是放弃线程:让它干净地完成但忽略它的结果。

    一如既往,我推荐阅读Joe Albahari's free ebook

    【讨论】:

    • Hrm...如果我不中止它们,您最终可能会打开几十个线程。那,我需要编写一些特殊的代码来确保“回调”不会不同步。
    【解决方案2】:

    使用 Thread.Abort() 中止线程是不安全的。但是您可以改为实现自己的中止,这可以让您以受控方式安全地关闭线程。

    如果您使用 EnumerateFiles 而不是 GetFiles,则可以在递增计数器时循环遍历每个文件以获取文件总数,同时检查标志以查看线程是否需要中止。

    调用诸如此类的东西代替您当前的 GetFiles().Length:

    private bool AbortSearch = false;
    private int NumberOfFiles(string dir, string searchPattern, SearchOption searchOption)
    {
        var files = Directory.EnumerateFiles(dir, searchPattern, searchOption);
        int numberOfFiles = 0;
        foreach (var file in files)
        {
            numberOfFiles++;
    
            if (AbortSearch)
            {
                break;
            }
        }
        return numberOfFiles;
    }
    

    然后你可以替换

    _fileThread.Abort();
    

    与

    AbortSearch=true;
    _fileThread.Join();
    

    您将使用当前的 Thread.Abort() 实现您的目标,但您将允许所有线程在您希望它们干净地结束时结束。

    【讨论】:

    • 我正在考虑这个问题,但你会注意到我在那里有一个.OrderBy。我认为它必须在返回第一个结果之前通过所有这些,这将是大部分时间,因此这不会产生任何好处....但是现在我考虑一下,我想我可以让它通过 2 次,然后首先获取所有文件(通过枚举)。 ..手动将它们放入列表中,然后对后缀进行排序。
    • 我认为,如果您可以将 GetFiles 的两个实例都转换为 EnumerateFiles,您将不再有需要花费大量时间执行的单行代码,因此应该能够快速中止。也许您甚至可以尝试在一个组合循环中完成所有操作(使用文件计数器),这样您只需要遍历文件名一次?
    • 是的,我枚举所有文件并手动进行过滤,然后我只需要循环一次。也许我会试试看。
    猜你喜欢
    • 1970-01-01
    • 2020-07-14
    • 2021-07-12
    • 2020-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-03
    相关资源
    最近更新 更多