C# 如何判断线程是否已成功退出?

C# 如何判断线程是否已成功退出?,c#,multithreading,thread-state,C#,Multithreading,Thread State,根据,可以通过以下两种方式之一进入停止状态:退出线程或中止线程 是否有某种机制通过正常退出来判断线程是否已进入停止状态?谢谢 线程只能通过调用thread.Abort()中止,这会导致ThreadAbortException,因此通过异常处理,您应该能够确定正常退出与中止退出。您可能需要查看BackgroundWorker类。当线程完成时,它有一个通用事件处理程序。在这里,您可以检查线程是否由于错误而完成,是因为它已被取消还是因为它已成功完成。线程可以通过几种方式达到停止状态: 它的主要方法可

根据,可以通过以下两种方式之一进入停止状态:退出线程或中止线程


是否有某种机制通过正常退出来判断线程是否已进入停止状态?谢谢

线程只能通过调用thread.Abort()中止,这会导致ThreadAbortException,因此通过异常处理,您应该能够确定正常退出与中止退出。

您可能需要查看BackgroundWorker类。当线程完成时,它有一个通用事件处理程序。在这里,您可以检查线程是否由于错误而完成,是因为它已被取消还是因为它已成功完成。

线程可以通过几种方式达到停止状态:

  • 它的主要方法可以在没有任何错误的情况下退出
  • 线程上的未捕获异常可以终止它
  • 另一个线程可以调用thread.Abort(),这将导致在该线程上引发ThreadAbortException
我不知道您是否想要区分这三种状态,但如果您真正感兴趣的是线程是否成功完成,我建议使用某种共享数据结构(同步字典可以工作),线程的主循环在终止时更新。您可以将ThreadName属性用作此共享字典中的键。对终止状态感兴趣的其他线程可以从该字典中读取以确定线程的最终状态

在进一步查看之后,您应该能够使用
ThreadState
属性区分外部中止的线程。当线程响应Abort()调用时,应将其设置为
ThreadState.Aborted
。然而,除非您能够控制所运行的线程代码,否则我认为您无法区分刚刚退出其主方法的线程和以异常终止的线程


但是请记住,如果您控制启动线程的代码,则始终可以替换您自己的方法,该方法在内部调用运行主线程逻辑的代码,并在那里注入异常检测(如上所述)。

假设主线程需要等待工作线程成功完成,我通常使用手动重置事件。或者,对于多个工作线程,来自Parallels扩展的新CountDownEvent,例如。

是要监视代码的线程吗?如果是这样,您可以将整个事件包装在一个类中,并在完成时引发一个事件,或者使用WaitHandle(我将使用ManualResetEvent)来表示完成。-完全封装后台逻辑。您还可以使用此封装捕获异常,然后引发事件

大概是这样的:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace BackgroundWorker
{
    public class BackgroundWorker
    {
        /// 
        /// Raised when the task completes (you could enhance this event to return state in the event args)
        /// 
        public event EventHandler TaskCompleted;

        /// 
        /// Raised if an unhandled exception is thrown by the background worker
        /// 
        public event EventHandler BackgroundError;

        private ThreadStart BackgroundTask;
        private readonly ManualResetEvent WaitEvent = new ManualResetEvent(false);

        /// 
        /// ThreadStart is the delegate that  you want to run on your background thread.
        /// 
        /// 
        public BackgroundWorker(ThreadStart backgroundTask)
        {
            this.BackgroundTask = backgroundTask;
        }

        private Thread BackgroundThread;

        /// 
        /// Starts the background task
        /// 
        public void Start()
        {
            this.BackgroundThread = new Thread(this.ThreadTask);
            this.BackgroundThread.Start();

        }

        private void ThreadTask()
        {
            // the task that actually runs on the thread
            try
            {
                this.BackgroundTask();
                // completed only fires on successful completion
                this.OnTaskCompleted(); 
            }
            catch (Exception e)
            {
                this.OnError(e);
            }
            finally
            {
                // signal thread exit (unblock the wait method)
                this.WaitEvent.Set();
            }

        }

        private void OnTaskCompleted()
        {
            if (this.TaskCompleted != null)
                this.TaskCompleted(this, EventArgs.Empty);
        }

        private void OnError(Exception e)
        {
            if (this.BackgroundError != null)
                this.BackgroundError(this, new BackgroundWorkerErrorEventArgs(e));
        }

        /// 
        /// Blocks until the task either completes or errrors out
        /// returns false if the wait timed out.
        /// 
        /// Timeout in milliseconds, -1 for infinite
        /// 
        public bool Wait(int timeout)
        {
            return this.WaitEvent.WaitOne(timeout);
        }

    }


    public class BackgroundWorkerErrorEventArgs : System.EventArgs
    {
        public BackgroundWorkerErrorEventArgs(Exception error) { this.Error = error; }
        public Exception Error;
    }

}
注意:您需要一些代码来防止Start被调用两次,并且您可能需要添加一个state属性来向线程传递state

下面是一个控制台应用程序,演示了此类的使用:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BackgroundWorker
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Test 1");
            BackgroundWorker worker = new BackgroundWorker(BackgroundWork);
            worker.TaskCompleted += new EventHandler(worker_TaskCompleted);
            worker.BackgroundError += new EventHandler(worker_BackgroundError);
            worker.Start();
            worker.Wait(-1);

            Console.WriteLine("Test 2");
            Console.WriteLine();

            // error case
            worker = new BackgroundWorker(BackgroundWorkWithError);
            worker.TaskCompleted += new EventHandler(worker_TaskCompleted);
            worker.BackgroundError += new EventHandler(worker_BackgroundError);
            worker.Start();
            worker.Wait(-1);

            Console.ReadLine();
        }

        static void worker_BackgroundError(object sender, BackgroundWorkerErrorEventArgs e)
        {
            Console.WriteLine("Exception: " + e.Error.Message);
        }

        private static void BackgroundWorkWithError()
        {
            throw new Exception("Foo");
        }

        static void worker_TaskCompleted(object sender, EventArgs e)
        {
            Console.WriteLine("Completed");
        }

        private static void BackgroundWork()
        {
            Console.WriteLine("Hello!");
        }
    }
}


我使用
CancellationTokenSource
请求线程优雅地退出

然后我使用
var exitedregly=\u thread.Join(TimeSpan.FromSeconds(10);
等待线程退出

如果
正确退出==false
,我会将错误推送到日志中


我主要在
Dispose()中使用此模式
函数,我正在尝试清理我创建的所有线程。

据我所知,ThreadAbortException仅在实际中止的线程中出现,而不是在调用代码中出现。因此,我无法在调用代码中捕获该异常。@pwnstein这是正确的。我假设您可以控制线程代码。我如果你只能访问调用代码,那么我的回答对你没有任何用处。有趣的是,你应该提到……我实际上实现了BackgroundWorker类的另一种风格——可以取消长时间运行的操作。不过,感谢你的建议!我的理解是,线程运行后台Worker实例实际上不会在Worker完成时终止。您可以确定Worker何时完成其工作,但这与线程终止时不同。现在,考虑到这个问题,这可能是一种确定工作任务状态的可行技术……但值得指出其区别。您可以取消长时间运行使用默认后台工作线程调用ng进程。只需调用CancelAsync。然后,在工作线程中,您必须查找CancelationPending标志,该标志在我感兴趣的情况下不起作用-一个后台线程完成可能长期运行的网络操作(即检索SQL Server列表).据我所知,CancelAsync为您在处理代码中手动处理设置了一个标志,如果您的处理代码是一个阻塞调用,则这对您没有帮助。相信我,这是我看到的第一件事。:)谢谢!