c# - descargar - zombi pc requisitos
La cancelaciĆ³n de subprocesos deja transacciones de zombis y SqlConnection roto (2)
Este es un error en la implementación MARS de Microsoft. Desactivar MARS en su cadena de conexión hará que el problema desaparezca.
Si necesita MARS y se siente cómodo al hacer que su aplicación dependa de la implementación interna de otra compañía, familiarícese con http://dotnet.sys-con.com/node/39040 , divida .NET Reflector y observe la conexión y el pool clases Debe almacenar una copia de la propiedad DbConnectionInternal antes de que se produzca el error. Más adelante, use la reflexión para pasar la referencia a un método de desasignación en la clase de agrupación interna. Esto evitará que su conexión se prolongue durante 4:00 - 7:40 minutos.
Seguramente hay otras formas de forzar la conexión fuera de la piscina y ser eliminada. Sin embargo, a falta de un hotfix de Microsoft, la reflexión parece ser necesaria. Los métodos públicos en la API de ADO.NET no parecen ayudar.
Siento que este comportamiento no debería estar sucediendo. Aquí está el escenario:
Iniciar una transacción de SQL de larga duración.
El hilo que ejecutó el comando sql se aborta (¡no por nuestro código!)
Cuando el hilo vuelve al código administrado, el estado de SqlConnection está "Cerrado", pero la transacción aún está abierta en el servidor SQL.
El SQLConnection puede volver a abrirse y puede intentar revertir la transacción, pero no tiene ningún efecto (no es de esperar que ocurra este comportamiento. El punto es que no hay manera de acceder a la transacción en la base de datos y rodarla). atrás.)
El problema es simplemente que la transacción no se limpia correctamente cuando el hilo se aborta. Este fue un problema con .Net 1.1, 2.0 y 2.0 SP1. Estamos ejecutando .Net 3.5 SP1.
Aquí hay un programa de ejemplo que ilustra el problema.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Threading;
namespace ConsoleApplication1
{
class Run
{
static Thread transactionThread;
public class ConnectionHolder : IDisposable
{
public void Dispose()
{
}
public void executeLongTransaction()
{
Console.WriteLine("Starting a long running transaction.");
using (SqlConnection _con = new SqlConnection("Data Source=<YourServer>;Initial Catalog=<YourDB>;Integrated Security=True;Persist Security Info=False;Max Pool Size=200;MultipleActiveResultSets=True;Connect Timeout=30;Application Name=ConsoleApplication1.vshost"))
{
try
{
SqlTransaction trans = null;
trans = _con.BeginTransaction();
SqlCommand cmd = new SqlCommand("update <YourTable> set Name = ''XXX'' where ID = @0; waitfor delay ''00:00:05''", _con, trans);
cmd.Parameters.Add(new SqlParameter("0", 340));
cmd.ExecuteNonQuery();
cmd.Transaction.Commit();
Console.WriteLine("Finished the long running transaction.");
}
catch (ThreadAbortException tae)
{
Console.WriteLine("Thread - caught ThreadAbortException in executeLongTransaction - resetting.");
Console.WriteLine("Exception message: {0}", tae.Message);
}
}
}
}
static void killTransactionThread()
{
Thread.Sleep(2 * 1000);
// We''re not doing this anywhere in our real code. This is for simulation
// purposes only!
transactionThread.Abort();
Console.WriteLine("Killing the transaction thread...");
}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
using (var connectionHolder = new ConnectionHolder())
{
transactionThread = new Thread(connectionHolder.executeLongTransaction);
transactionThread.Start();
new Thread(killTransactionThread).Start();
transactionThread.Join();
Console.WriteLine("The transaction thread has died. Please run ''select * from sysprocesses where open_tran > 0'' now while this window remains open. /n/n");
Console.Read();
}
}
}
}
Existe un Hotfix de Microsoft dirigido a .Net2.0 SP1 que se suponía que iba a solucionar este problema , pero obviamente tenemos DLL más nuevos (.Net 3.5 SP1) que no coinciden con los números de versión que se enumeran en este hotfix.
¿Alguien puede explicar este comportamiento y por qué ThreadAbort todavía no está limpiando la transacción de SQL correctamente? ¿.Net 3.5 SP1 no incluye este hotfix, o este comportamiento es técnicamente correcto?
Ya que está utilizando SqlConnection
con la agrupación, su código nunca tiene el control de cerrar las conexiones. La piscina es. En el lado del servidor, una transacción pendiente se retrotraerá cuando la conexión esté realmente cerrada (socket cerrado), pero al agrupar el lado del servidor nunca se cierra la conexión. Sin el cierre de la conexión (ya sea por una desconexión física en la capa socket / pipe / LPC o por sp_reset_connection
llamada sp_reset_connection
), el servidor no puede abortar la transacción pendiente. Así que realmente se reduce al hecho de que la conexión no se libera / reinicia correctamente. No entiendo por qué está tratando de complicar el código con la anulación explícita del aborto y el intento de volver a abrir una transacción cerrada (que nunca funcionará). Simplemente debe envolver SqlConnection en un bloque using(...)
, el final implícito y la conexión Dispose se ejecutarán incluso en el aborto de subprocesos.
Mi recomendación sería mantener las cosas simples, deshacerse del manejo del aborto del hilo de fantasía y reemplazarlo con un bloque ''using'' simple (using(connection) {using(transaction) {code; commit () }}
.
Por supuesto, asumo que no propaga el contexto de la transacción en un ámbito diferente en el servidor (no usa sp_getbindtoken
y amigos, y no se inscribe en transacciones distribuidas).
Este pequeño programa muestra que Thread.Abort cierra correctamente una conexión y la transacción se retrotrae:
using System;
using System.Data.SqlClient;
using testThreadAbort.Properties;
using System.Threading;
using System.Diagnostics;
namespace testThreadAbort
{
class Program
{
static AutoResetEvent evReady = new AutoResetEvent(false);
static long xactId = 0;
static void ThreadFunc()
{
using (SqlConnection conn = new SqlConnection(Settings.Default.conn))
{
conn.Open();
using (SqlTransaction trn = conn.BeginTransaction())
{
// Retrieve our XACTID
//
SqlCommand cmd = new SqlCommand("select transaction_id from sys.dm_tran_current_transaction", conn, trn);
xactId = (long) cmd.ExecuteScalar();
Console.Out.WriteLine("XactID: {0}", xactId);
cmd = new SqlCommand(@"
insert into test (a) values (1);
waitfor delay ''00:01:00''", conn, trn);
// Signal readyness and wait...
//
evReady.Set();
cmd.ExecuteNonQuery();
trn.Commit();
}
}
}
static void Main(string[] args)
{
try
{
using (SqlConnection conn = new SqlConnection(Settings.Default.conn))
{
conn.Open();
SqlCommand cmd = new SqlCommand(@"
if object_id(''test'') is not null
begin
drop table test;
end
create table test (a int);", conn);
cmd.ExecuteNonQuery();
}
Thread thread = new Thread(new ThreadStart(ThreadFunc));
thread.Start();
evReady.WaitOne();
Thread.Sleep(TimeSpan.FromSeconds(5));
Console.Out.WriteLine("Aborting...");
thread.Abort();
thread.Join();
Console.Out.WriteLine("Aborted");
Debug.Assert(0 != xactId);
using (SqlConnection conn = new SqlConnection(Settings.Default.conn))
{
conn.Open();
// checked if xactId is still active
//
SqlCommand cmd = new SqlCommand("select count(*) from sys.dm_tran_active_transactions where transaction_id = @xactId", conn);
cmd.Parameters.AddWithValue("@xactId", xactId);
object count = cmd.ExecuteScalar();
Console.WriteLine("Active transactions with xactId {0}: {1}", xactId, count);
// Check count of rows in test (would block on row lock)
//
cmd = new SqlCommand("select count(*) from test", conn);
count = cmd.ExecuteScalar();
Console.WriteLine("Count of rows in text: {0}", count);
}
}
catch (Exception e)
{
Console.Error.Write(e);
}
}
}
}