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

发布于 2024-07-24 06:25:30 字数 230 浏览 12 评论 0原文

根据 ThreadState 的 MSDN 文档,已停止可以通过以下两种方式之一进入状态:线程退出或线程被中止。

是否有某种机制可以判断线程是否通过正常退出而进入 Stopped 状态? 谢谢!

According to the MSDN Documentation for ThreadState, the Stopped state can be entered by one of two ways: the thread exiting, or the thread being aborted.

Is there some mechanism for telling whether a thread has entered the Stopped state by exiting normally? Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

吖咩 2024-07-31 06:25:31

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

  • 它的 main 方法可以退出而没有任何错误。
  • 线程上未捕获的异常可以终止它。
  • 另一个线程可以调用 Thread.Abort(),这将导致该线程抛出 ThreadAbortException。

我不知道您是否想要区分所有三种状态,但如果您真正感兴趣的是线程是否成功完成,我建议使用某种共享数据结构(同步字典可以工作)线程的主循环在终止时更新。 您可以使用 ThreadName 属性作为此共享字典中的键。 对终止状态感兴趣的其他线程可以从此字典中读取以确定线程的最终状态。

详细查看 MSDN 文档后,您应该能够使用 ThreadState 属性区分外部中止的线程。 当线程响应 Abort() 调用时,应将其设置为 ThreadState.Aborted。 但是,除非您可以控制运行的线程代码,否则我认为您无法区分刚刚退出其 main 方法的线程和因异常终止的线程。

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

A thread can reach the Stopped state in several ways:

  • It's main method can exit without any errors.
  • An uncaught exception on the thread can terminate it.
  • Another thread could call Thread.Abort(), which will cause a ThreadAbortException to be thrown on that thread.

I don't know if you are looking to differentiate between all three states, but if all you are really interested in is whether the thread completed successfully, I would suggest using a shared data structure of some kind (a synchronized dictionary would work) that the main loop of a thread updates as it terminates. You can use the ThreadName property as the key in this shared dictionary. Other threads that are interested in termination state, could read from this dictionary to determine the final status of the thread.

After looking a bit more at the MSDN documentation, you should be able to differentiate an externally aborted thread using the ThreadState property. This should be set to ThreadState.Aborted when a thread responds to the Abort() call. However, unless you have control of the thread code that is run, I don't think that you can differentiate between a thread that just exited its main method, vs one that terminated with an exception.

Keep in mind however, if you control the code that starts the thread, you can always substitute your own method that internally calls the code that runs the main thread logic and inject exception detection (as I described above) there.

≈。彩虹 2024-07-31 06:25:31

是您想要监视代码的线程吗? 如果是这样,您可以将整个事情包装在一个类中,并在完成时引发一个事件或使用 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 被调用两次,并且您可能需要添加一个状态属性来将状态传递给您的线程。

这是一个控制台应用程序,演示了此类的用法:


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!");
        }
    }
}


Is the thread that you want to monitor your code? If so, you could wrap the whole thing in a class, and either raise an event when you finish or use a WaitHandle (I would use a ManualResetEvent) to signal completion. -- completely encapsulate the background logic. You can also use this encapsulation to capture an exception and then raise an event.

Something like this:


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;
    }

}

Note: you need some code to keep Start from being called twice, and you might want to add a state property for passing state to your thread.

Here's a console app that demonstrates the use of this class:


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!");
        }
    }
}


森罗 2024-07-31 06:25:31

您可能想查看BackgroundWorker 类。 它有一个线程完成时的通用事件处理程序。 在那里您可以检查线程是否由于错误而完成、因为它被取消或因为它成功完成。

You may want to look at the BackgroundWorker class. It has a generic event handler for when the thread completes. In there you can check if the thread completed due to an error, because it was canceled or because it finished successfully.

望她远 2024-07-31 06:25:31

假设主线程需要等待工作线程成功完成,我通常使用ManualResetEvent。 或者,对于多个工作线程,Parallels Extensions 中的新 CountDownEvent,例如 所以

Assuming the main thread needs to wait for the worker thread to complete successfully, I usually use ManualResetEvent. Or, for multiple worker threads, the new CountDownEvent from Parallels Extensions, like so.

终陌 2024-07-31 06:25:31

我使用 CancellationTokenSource 来要求线程正常退出。

然后,我使用 var exitedProperly = _thread.Join(TimeSpan.FromSeconds(10); 等待线程退出。

如果 exitedProperly==false,我会将错误推送到 我主要

在使用 Dispose() 函数时使用此模式,并且尝试清理我创建的所有线程。

I use a CancellationTokenSource to ask the thread to exit gracefully.

I then use var exitedProperly = _thread.Join(TimeSpan.FromSeconds(10); which waits for the thread to exit.

If exitedProperly==false, I push an error into the log.

I use this pattern mainly when I am in the Dispose() function, and I am trying to clean up any threads I have created.

口干舌燥 2024-07-31 06:25:31

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

A thread can only be aborted by calling Thread.Abort(), which results in a ThreadAbortException, so through exception handling you should be able to determine a normal exit vs an aborted exit.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文