在'using'块中中止线程会处理已使用的实例吗?

4

我要开启这样的一个线程:

nameOfThread = new Thread(() => 
{
    //do stuff
});
nameOfThread.Start();

在这个匿名函数中的某一点,我会像这样打开WinSCP会话:

using (Session session = new Session())
{
     //do stuff
}

如果我在using内部仍在执行任务,同时从其他地方终止线程,例如nameOfThread.Abort(),那么会话是否在最后被清理?

2
为什么要中止线程?使用CancellationTokenSource并优雅地向工作方法发出信号,表明它需要终止。 - Panagiotis Kanavos
是的,无论是否中止,using语句仍然正确处理清理工作。 - Michael Puckett II
你为什么直接使用线程而不是例如 Task.Run? - Panagiotis Kanavos
@Mr.Howell ,大量的.NET方法也接受取消令牌(例如数据库命令),这意味着您可以取消当前正在运行的任何阻塞操作,而不会中止线程。 - Panagiotis Kanavos
@PanagiotisKanavos 好的,我会研究一下的,因为我显然对这方面的知识不够了解,谢谢。 - Angelo
显示剩余4条评论
2个回答

3
很可能会有影响,但是不能确定

根据文档:

当在线程上调用此方法 [Abort] 时,系统会在该线程中抛出 ThreadAbortException 来终止它。

我们知道异常仍然会让 using 语句正常执行,这也是应该的。(除了一些例外情况)

另一方面,如果您可以优雅地结束线程,例如使用 CancellationTokenSource,这对于您的应用程序来说会更好。它将提供更多控制实际终止线程和处理异常的方式。


请记住,线程可能会调用 ResetAbort 并阻止 ThreadAbortException 的影响。 - Brian Rasmussen
1
你会在这篇帖子中发现不便之真相。 - Hans Passant
@HansPassant 这应该不再是真的了。我记得在较早版本的.NET中已经解决了这个问题,不确定是哪个版本,在现在的版本中,try / finally嵌套在另一个try / finally中,并进行一些其他线程管理来处理它。不要让OP感到困惑,但这是一些幕后工作,以确保这种情况不再发生。从当前.NET版本来看,没有竞态条件的情况下,应该被解决为准确和线程安全的。我需要回头看看这是什么时候更新的。 - Michael Puckett II
这是一个有趣的细节,值得更好的注释,因为在我的 .NET 4.7.2 和 C# v7.3 中还没有发生。 - Hans Passant
你可以设置超时时间,在它自己没有结束的情况下结束它。至少,这样可以让它有机会优雅地结束。 - Patrick Hofman
显示剩余7条评论

1
我曾回答过你可以保证using语句总是会调用Dispose,但我现在认识到这是错误的,我错了。 using语句存在潜在的竞争条件,不能保证处理完毕,我编写了一个控制台应用程序来说明这一点(这并不难或琐碎)。
当展示IL如何生成using时,我的观点是正确的:
var session = new Session(); //If this causes an error or abort happens during initialization then we don't enter try
//If abort is called here then we never enter try
//In either case above we may have undisposed resources initialized at this point
try
{
    //do stuff
}
finally
{
    session.Dispose();
}   

然而请注意,如果在进入"try"之前被中止可能会发生竞态条件的评论。这里有一个控制台应用程序,只是为了证明这一点。第一个按预期工作,但如果在初始化"R"时添加注释代码"//thread.Abort()",那么你将看到它被初始化但从未处理:/。
using System;
using System.Threading;

namespace Question_Answer_Console_App
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Start Main");

            Thread thread = null;
            thread = new Thread(new ThreadStart(() =>
            {
                Console.WriteLine("Thread Started");
                using (var r = new R(thread))
                {
                    Console.WriteLine($"Using {nameof(R)}");
                }
            }));

            thread.Start();
            thread.Join();

            Console.WriteLine("End Main");
            Console.ReadKey();
        }
    }

    public class R : IDisposable
    {
        public R(Thread thread)
        {
            Console.WriteLine($"Init {nameof(R)}");
            //thread.Abort();
        }

        public void Dispose()
        {
            Console.WriteLine($"Disposed {nameof(R)}");
        }
    }
}

//thread.Abort()注释掉后的输出:

Start Main
Thread Started
Init R
Using R
Disposed R
End Main
使用thread.Abort()未被注释的输出:
Start Main
Thread Started
Init R
End Main

我认为问题不在于using如何工作,而在于线程中止对其的影响。 - Patrick Hofman
@PatrickHofman 没错。也许我应该解释得更清楚,但我确实提到了即使线程被中止,它仍然会调用finally,因为它基本上被翻译成了一个try finally,所以当被中止时会被处理。 - Michael Puckett II
@PatrickHofman 我在底部更新了答案,希望更加清晰明了。 - Michael Puckett II

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接