un fil peut-il jamais jeter plus d'un ThreadAbortException?
-
28-09-2019 - |
Question
Je ne suis pas sûr que c'est le forum approprié pour ce type de question, mais je suis en train d'essayer de trouver un bug, je ne peux pas reproduis dans un service Web à l'aide d'un vidage de la mémoire et je pense que je pose une question précise, je avez besoin d'aide, que Je pense que quelqu'un pourrait avoir une entrée sur.
L'analyse d'un vidage de la mémoire en utilisant WinDbg Je trouve aprox 75000 ThreadAbortExceptions en mémoire, et ils proviennent tous d'ici:
at System.Threading.WaitHandle.WaitOne(Int64 timeout Boolean exitContext)
at MyNameSpace.CustomThreadPool.Run()
Ils sont tous créés dans un très court laps de temps, lorsque l'application tente de décharger sa appdomain (IIS ferme vers le bas).
Ce que je ne peux pas savoir est en ce moment comment il est possible de soulever tant de ThreadAbortExceptions? Si un départ volontaire de fil, est-il possible qu'il peut soulever plus d'un? Si quelqu'un peut donner une indication quant à la raison pour laquelle tant d'exceptions de ce type peuvent exister? D'après ce que je peux voir il y a environ 20 threads max est ce processus, et le threadpool lui-même n'a qu'un seul fil (!) Lorsque cela se produit.
La classe CustomThreadPool vient de cet 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;
}
}
La solution
Il est possible d'attraper puis réinitialiser un ThreadAbortException
en utilisant Thread.ResetAbort
. Ainsi, un seul fil pourrait avoir fait beaucoup de ces exceptions lancées à ce sujet.
Par exemple, si vous appelez Response.Redirect(url, true)
dans ASP.NET, il abandonnera le fil en cours, puis annuler l'abandon plus haut.
Je ne sais pas ce qui explique tout à fait votre situation, mais il est intéressant de regarder. Sinon, est quelque chose d'essayer de recréer le pool de threads quand il « se bloque » en raison du domaine de l'application Déchargement?
EDIT: Pour répondre à votre commentaire: selon le AppDomain.Unload
documentation :
Les fils de domaine sont terminées en utilisant la méthode Abort, qui jette un ThreadAbortException dans le fil. Bien que le fil doit se terminer rapidement, il peut continuer à exécuter pour une quantité imprévisible de temps une clause finally.
Fondamentalement, les fils sont précisément avortés parce que votre appdomain est déchargé.
Autres conseils
Faire un Response.Redirect ( "~ / Somewhere.aspx") provoque parfois un ThreadAbortException si le thread courant (par défaut) n'a pas fini de l'exécution encore.
Vous pouvez éviter cela en utilisant la méthode de redirection surchargée.
Response.Redirect("~/Somewhere.aspx", false);