【问题标题】:does aborting a thread while in 'using' block dispose used instance在“使用”块处理使用的实例时中止线程
【发布时间】:2018-11-27 13:25:12
【问题描述】:

我正在开始这样的线程:

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

在这个匿名函数中的某个时刻,我打开了一个 WinSCP 会话,如下所示:

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

如果我像 nameOfThread.Abort() 这样中止线程(从其他地方),同时仍在 using 内部做事,会话是否在最后被处理?

【问题讨论】:

  • 为什么要终止线程?使用 CancellationTokenSource 并优雅地向需要终止的工作方法发出信号
  • 是的,无论是否中止 using 语句仍然可以正确处理。
  • 你为什么直接使用线程而不是Task.Run?
  • 如果您遇到问题并认为Thread.Abort 是解决方案的一部分,那么您找到了错误的解决方案。注意documentation中的警告
  • @PanagiotisKanavos 因为在多线程方面我并不精通。但这并不能回答问题

标签: c# multithreading


【解决方案1】:

很可能会,但you can't be sure

根据文档:

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

而且我们知道异常仍然会让using 语句按照它们应该的方式处理。 (给予和接受一些例外)

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

【讨论】:

  • 好的,感谢您的回答和替代方案。我会多研究多线程。
  • 请记住,线程可以调用ResetAbort 并阻止ThreadAbortException 的影响。
  • 你会在this post找到不方便的真相。
  • @HansPassant 这应该不再是真的。我记得在早期版本的 .NET 中已经解决了这个问题,不确定是哪个,现在 try / finally 嵌套在另一个 try / finally 中,并有一些其他线程管理来处理它。不要混淆 OP,但需要在幕后工作以确保不再发生这种情况。照原样看,在当前的 .NET 版本中,如果没有竞争条件,则应该将其解析为准确且线程安全的。我得回头看看它是什么时候更新的。
  • 这是一个有趣的花絮,值得更好的注释,因为这在我的 .NET 4.7.2 和 C# v7.3 的机器上还没有发生。
【解决方案2】:

我回答您可以保证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 之前显示中止可能发生的竞争条件的 cmets。

这是一个控制台应用程序,只是为了证明这一点。第一个按预期工作,但如果您在我们初始化 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 的工作原理,而在于线程中止对它的影响。
  • @PatrickHofman 你是对的。也许我应该解释得更好,但我确实提到了一个线程,即使在中止时,总是会调用finally,因为它基本上被翻译成try finally,它会在中止时处理。
  • @PatrickHofman 我更新了底部的答案,希望能更清楚。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多