C# 在';使用';块处理已用实例

C# 在';使用';块处理已用实例,c#,multithreading,C#,Multithreading,我正在开始这样一个线程: nameOfThread = new Thread(() => { //do stuff }); nameOfThread.Start(); using (Session session = new Session()) { //do stuff } 在这个匿名函数中的某个时刻,我打开了一个WinSCP会话,如下所示: nameOfThread = new Thread(() => { //do stuff }); name

我正在开始这样一个线程:

nameOfThread = new Thread(() => 
{
    //do stuff
});
nameOfThread.Start();
using (Session session = new Session())
{
     //do stuff
}
在这个匿名函数中的某个时刻,我打开了一个WinSCP会话,如下所示:

nameOfThread = new Thread(() => 
{
    //do stuff
});
nameOfThread.Start();
using (Session session = new Session())
{
     //do stuff
}
如果我像这样中止线程(从其他地方)nameOfThread.abort()

根据文件:

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

我们知道,异常仍然会让
使用
语句进行处理,这是它们应该做的。(给出并接受一些例外情况)


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

我回答说,您可以保证
using
语句将始终调用
Dispose
,并且我得到纠正,我错了

有一个潜在的竞争条件,即
使用的
语句不能保证处置
,我已经编写了一个控制台应用程序来说明这一点(这不是很难或很琐碎)

我正确地展示了IL如何使用
生成
,如下所示:

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()
,则您将看到它init,但不会dispose:/

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
Start Main
Thread Started
Init R
End Main
带有
线程的输出。中止()
未注释掉:

Start Main
Thread Started
Init R
Using R
Disposed R
End Main
Start Main
Thread Started
Init R
End Main

为什么中止线程?使用CancellationTokenSource并优雅地向worker方法发出信号,表示它需要终止眼睛,无论是否中止,
using
语句仍能正确处理。为什么要直接使用线程而不是例如Task.Run?如果您有问题并认为
thread.Abort
是解决方案的一部分,你找到了错误的解决方案。请注意@PanagiotisKanavos中的警告,因为在多线程方面,我并不是一个真正的专家。但这并不能回答问题好吧,谢谢你的回答和选择。我将进一步研究多线程。请记住,线程可以调用
ResetAbort
,并防止
ThreadAbortException
的影响。您将在中找到不方便的事实。@HansPassant这应该不再是真的。我记得读过的.NET的早期版本解决了这个问题,不确定是哪个版本,现在
try/finally
嵌套在另一个
try/finally
中,由其他线程管理来处理。不要混淆OP,但这是一个幕后工作,以确保这种情况不再发生。照现在的情况看,在当前的.NET版本中,没有竞争条件的情况下,应该以准确和线程安全的方式解决。我必须回过头来看看这是什么时候更新的。这是一个有趣的小道消息,值得更好的注释,因为这在我的.NET 4.7.2和C#v7.3中的机器上还没有发生。我认为问题不在于
如何使用
工作,而在于线程中止如何对其产生影响。@Patrickhoffman你是对的。也许我应该解释得更好,但我确实提到了一个线程,即使被中止,也总是调用
finally
,因为它基本上被翻译成
try finally
,所以在中止时它会被处理。@Patrickhoffman我更新了底部的答案,希望能让它更清楚。