一个线程是否可以抛出多个 ThreadAbortException?

发布于 2024-09-06 17:34:07 字数 2655 浏览 10 评论 0 原文

我不确定这是否是此类问题的正确论坛,但我目前正在尝试找到一个我无法使用内存转储在网络服务中重现的错误,并且我认为我有一个需要帮助的特定问题,我认为有人可能有一些意见。

使用 WinDbg 分析内存转储,我发现内存中大约有 75000 个 ThreadAbortException,它们都源自这里:

at System.Threading.WaitHandle.WaitOne(Int64 timeout  Boolean exitContext)
at MyNameSpace.CustomThreadPool.Run()

它们都是在应用程序尝试卸载其应用程序域(IIS 正在关闭)时在很短的时间内创建的。

我现在不明白的是怎么可能引发这么多 ThreadAbortExceptions?如果一个线程退出,有什么方法可以引发多个线程吗?如果有人可以给出任何提示,说明为什么会存在如此多的这种类型的异常?据我所知,这个进程最多有大约 20 个线程,而当发生这种情况时,线程池本身只有一个(!)线程。

CustomThreadPool类来自这篇文章: http://msdn.microsoft.com/en-us/magazine/cc163851。 ASPX

public sealed class CustomThreadPool : IDisposable
{
    private Semaphore _workWaiting;
    private Queue<WaitQueueItem> _queue;
    private List<Thread> _threads;

    public CustomThreadPool(int numThreads)
    {
        if (numThreads <= 0) 
            throw new ArgumentOutOfRangeException("numThreads");

        _threads = new List<Thread>(numThreads);
        _queue = new Queue<WaitQueueItem>();
        _workWaiting = new Semaphore(0, int.MaxValue);

        for (int i = 0; i < numThreads; i++)
        {
            Thread t = new Thread(Run);
            t.IsBackground = true;
            _threads.Add(t);
            t.Start;
        }
    }

    public void Dispose()
    {
        if (_threads != null)
        {
            _threads.ForEach(delegate(Thread t) { t.Interrupt(); });
            _threads = null;
        }
    }

    public void QueueUserWorkItem(WaitCallback callback, object state)
    {
        if (_threads == null) 
            throw new ObjectDisposedException(GetType().Name);
        if (callback == null) throw new ArgumentNullException("callback");

        WaitQueueItem item = new WaitQueueItem();
        item.Callback = callback;
        item.State = state;
        item.Context = ExecutionContext.Capture();

        lock(_queue) _queue.Enqueue(item);
        _workWaiting.Release();
    }

    private void Run()
    {
        try
        {
            while (true)
            {
                _workWaiting.WaitOne();
                WaitQueueItem item;
                lock(_queue) item = _queue.Dequeue();
                ExecutionContext.Run(item.Context, 
                    new ContextCallback(item.Callback), item.State);
            }
        }
        catch(ThreadInterruptedException){}
    }

    private class WaitQueueItem
    {
        public WaitCallback Callback;
        public object State;
        public ExecutionContext Context;
    }
}

Im not sure this is the correct forum for this type of question, but Im currently trying to find a bug I cant reproduce in a web service using a memory dump and I think I have a specific question I need help with, that I think someone might have some input on.

Analyzing a memory dump using WinDbg I find aprox 75000 ThreadAbortExceptions in memory, and they all originate from here:

at System.Threading.WaitHandle.WaitOne(Int64 timeout  Boolean exitContext)
at MyNameSpace.CustomThreadPool.Run()

They are all created in a very short period of time, when the application is trying to unload its appdomain (IIS is closing down).

What I cant figure out right now is how its possible to raise so many ThreadAbortExceptions? If a thread quits, is there any way it may raise more than one? If anyone can give any hint as to why so many exceptions of this type can exist? From what I can see there's about 20 threads max is this process, and the threadpool itself has only one(!) thread when this occurs.

The CustomThreadPool class comes from this article:
http://msdn.microsoft.com/en-us/magazine/cc163851.aspx

public sealed class CustomThreadPool : IDisposable
{
    private Semaphore _workWaiting;
    private Queue<WaitQueueItem> _queue;
    private List<Thread> _threads;

    public CustomThreadPool(int numThreads)
    {
        if (numThreads <= 0) 
            throw new ArgumentOutOfRangeException("numThreads");

        _threads = new List<Thread>(numThreads);
        _queue = new Queue<WaitQueueItem>();
        _workWaiting = new Semaphore(0, int.MaxValue);

        for (int i = 0; i < numThreads; i++)
        {
            Thread t = new Thread(Run);
            t.IsBackground = true;
            _threads.Add(t);
            t.Start;
        }
    }

    public void Dispose()
    {
        if (_threads != null)
        {
            _threads.ForEach(delegate(Thread t) { t.Interrupt(); });
            _threads = null;
        }
    }

    public void QueueUserWorkItem(WaitCallback callback, object state)
    {
        if (_threads == null) 
            throw new ObjectDisposedException(GetType().Name);
        if (callback == null) throw new ArgumentNullException("callback");

        WaitQueueItem item = new WaitQueueItem();
        item.Callback = callback;
        item.State = state;
        item.Context = ExecutionContext.Capture();

        lock(_queue) _queue.Enqueue(item);
        _workWaiting.Release();
    }

    private void Run()
    {
        try
        {
            while (true)
            {
                _workWaiting.WaitOne();
                WaitQueueItem item;
                lock(_queue) item = _queue.Dequeue();
                ExecutionContext.Run(item.Context, 
                    new ContextCallback(item.Callback), item.State);
            }
        }
        catch(ThreadInterruptedException){}
    }

    private class WaitQueueItem
    {
        public WaitCallback Callback;
        public object State;
        public ExecutionContext Context;
    }
}

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

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

发布评论

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

评论(2

红ご颜醉 2024-09-13 17:34:07

可以使用 “nofollow noreferrer”>Thread.ResetAbort。因此,单个线程实际上可能会抛出许多此类异常。

例如,如果您在 ASP.NET 中调用 Response.Redirect(url, true),它将中止当前线程,然后取消更高层的中止。

我不确定这是否能完全解释您的情况,但值得一看。或者,当由于应用程序域被卸载而“崩溃”时,是否尝试重新创建线程池?

编辑:要回复您的评论:按照 AppDomain.Unload 文档:

域中的线程被终止
使用 Abort 方法,该方法会抛出
线程中的ThreadAbortException。
尽管线程应该终止
及时,它可以继续执行
在不可预测的时间内
最后子句。

基本上,线程被中止正是因为您的应用程序域正在被卸载。

It's possible to catch and then reset a ThreadAbortException using Thread.ResetAbort. So a single thread could actually have many such exceptions thrown on it.

For example, if you call Response.Redirect(url, true) in ASP.NET, it will abort the current thread and then cancel the abort higher up.

I'm not sure this quite explains your situation, but it's worth looking at. Alternatively, is something trying to recreate the thread pool when it "crashes" due to the app domain being unloaded?

EDIT: To respond to your comment: as per the AppDomain.Unload documentation:

The threads in domain are terminated
using the Abort method, which throws a
ThreadAbortException in the thread.
Although the thread should terminate
promptly, it can continue executing
for an unpredictable amount of time in
a finally clause.

Basically the threads are being aborted precisely because your appdomain is being unloaded.

你的他你的她 2024-09-13 17:34:07

如果当前(默认)线程尚未完成执行,则执行 Response.Redirect("~/Somewhere.aspx") 有时会导致 ThreadAbortException。

您可以通过使用重载的重定向方法来防止这种情况。

Response.Redirect("~/Somewhere.aspx", false);

Doing a Response.Redirect("~/Somewhere.aspx") sometimes causes a ThreadAbortException if the current (default) thread hasn't finished execution yet.

You can prevent this by using the overloaded redirect method.

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