是否有一个事件会告诉我当前正在运行的线程是否在中途中止,不是因为用户调用了Abort
而是因为程序正在终止?
我需要关闭每个关闭的线程以及有关线程Id的信息。
我希望它会成为AppDomainUnload
事件,但the documentation对此说nilch。
当运行时停止后台线程因为进程正在关闭时,线程中不会抛出异常。但是,当线程因AppDomain.Unload方法卸载应用程序域而停止时,前台和后台线程都会抛出ThreadAbortException。
为了测试这个事实,我编写了这个小程序,它产生两个长时间运行的线程,其中一个是前台线程而另一个是后台线程,并立即继续退出当前进程,从而尝试卸载当前进程应用程序域。
这应该不仅调用DomainUnload
处理程序,还试图中止两个正在运行的线程,并向它们发送ThreadAbortException
。
但这一切都不会发生。
输出窗口显示以下输出:
[#10] [Foreground]Starting to do stuff.
The thread 0x183c has exited with code 0 (0x0).
[#9] [Worker]Starting to do stuff.
[#10] [Foreground]Slept for 5 seconds. Now finishing up the doing of stuff.
The thread 0x2954 has exited with code 0 (0x0).
The program '[11224] KnowAboutDyingThread.vshost.exe' has exited with code 0 (0x0).
这是我的代码:
using System;
using System.Diagnostics;
using System.Threading;
namespace KnowAboutDyingThread
{
// Source: https://msdn.microsoft.com/en-us/library/h339syd0%28v=vs.110%29.aspx
// When the runtime stops a background thread because the process is shutting down,
// no exception is thrown in the thread. However, when threads are stopped because the
// AppDomain.Unload method unloads the application domain, a ThreadAbortException is
// thrown in both foreground and background threads.
// I am trying that out
// I asked a question here about it: http://stackoverflow.com/questions/37552668/is-there-a-way-to-know-if-a-thread-is-being-aborted-because-the-program-is-termi
// Permalink: http://stackoverflow.com/q/37552668/303685
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.DomainUnload += CurrentDomain_DomainUnload;
ThreadPool.QueueUserWorkItem(Do);
var t = new Thread(Do);
t.Name = "Foreground";
t.Start();
Process.GetCurrentProcess().Close();
}
private static void CurrentDomain_DomainUnload(object sender, EventArgs e)
{
Debug.Print("Unloading current appdomain...");
}
static void Do(object state)
{
try
{
Debug.Print(string.Format($"[#{Thread.CurrentThread.ManagedThreadId}] [{Thread.CurrentThread.Name ?? "Worker"}]Starting to do stuff."));
Thread.Sleep(5000);
Debug.Print(string.Format($"[#{Thread.CurrentThread.ManagedThreadId}] [{Thread.CurrentThread.Name ?? "Worker"}]Slept for 5 seconds. Now finishing up the doing of stuff."));
}
catch(ThreadAbortException abort)
{
Debug.Print(string.Format($"[#{Thread.CurrentThread.ManagedThreadId}] [{Thread.CurrentThread.Name ?? "Worker"}]Do got ThreadAbortException: {abort.GetType().Name}: {abort.Message}"));
}
catch(Exception ex)
{
Debug.Print(string.Format($"[#{Thread.CurrentThread.ManagedThreadId}] [{Thread.CurrentThread.Name ?? "Worker"}]Do had an exception: {ex.GetType().Name}: {ex.Message}"));
}
}
}
}
[1]: https://msdn.microsoft.com/en-us/library/system.appdomain.domainunload%28v=vs.110%29.aspx
答案 0 :(得分:0)
后台线程不应该正常终止。
您将无法捕获任何异步异常。这也适用于前景线程。
在前台线程中,您将能够优雅地进入public class AnotherClass {
public AnotherClass(UniteConverter ac) {
ac.input.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
public void onTextChanged(CharSequence s, int start,
int before, int count) {
}
}
}
块,但不能进入catch块。
您可以做的最简单的事情可能是使用最终对象:
finally
输出:
using System;
using System.Diagnostics;
using System.Threading;
namespace ReliableStop
{
class Program
{
static void Main(string[] args)
{
Debug.WriteLine("-- Started Main. Thread: {0}", Thread.CurrentThread.ManagedThreadId);
var everStarted = new ManualResetEvent(false);
var t = new Thread(o =>
{
Debug.WriteLine("-- Thread entered here.");
everStarted.Set();
using (new PostMortemThreadDump())
{
try
{
Thread.Sleep(100);
}
catch
{
Debug.WriteLine("-- Attempt to catch everything.");
}
finally
{
Debug.WriteLine("-- Attempt to process finally.");
}
}
});
t.IsBackground = true;
t.Start();
// Check that the thread has started.
everStarted.WaitOne();
// ... Good bye, no need to kill, I will die on my own.
}
}
class PostMortemThreadDump : IDisposable
{
int threadId { get; }
public PostMortemThreadDump()
{
threadId = Thread.CurrentThread.ManagedThreadId;
}
~PostMortemThreadDump()
{
Dispose(false);
}
void Dispose(bool disposing)
{
if (disposing)
{
Debug.WriteLine("-- PostMortemThreadDump. Finished normally.");
}
else
{
Debug.WriteLine("-- PostMortemThreadDump. Thread: {0}", threadId);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
现在,如果你改为使用前台线程,你也会得到`finally块执行:
-- Started Main. Thread: 9
-- Thread entered here.
The thread 0x6fc8 has exited with code 0 (0x0).
The thread 0x12b4 has exited with code 0 (0x0).
-- PostMortemThreadDump. Thread: 10
The program '[25260] ReliableStop.vshost.exe' has exited with code 0 (0x0).
正如您所看到的,终结器在两种情况下运行,并且在前台线程中,您将执行-- Started Main. Thread: 9
-- Thread entered here.
The thread 0x4d54 has exited with code 0 (0x0).
The thread 0x20c8 has exited with code 0 (0x0).
-- Attempt to process finally.
-- PostMortemThreadDump. Finished normally.
The thread 0x6928 has exited with code 0 (0x0).
The program '[26328] ReliableStop.vshost.exe' has exited with code 0 (0x0).
块。
调用finally
不会影响此方案。