Isso é thread.abort () normal e seguro?

Eu criei um controle personalizado de preenchimento automático, quando o usuário pressiona uma tecla que consulta o servidor de database (usando o Remoting) em outro thread. Quando o usuário digita muito rápido, o programa deve cancelar a solicitação / thread executados anteriormente.

Eu anteriormente o implementei como AsyncCallback primeiro, mas eu acho complicado, muitas regras da casa a seguir (por exemplo, AsyncResult, AsyncState, EndInvoke) e você precisa detectar o thread do object BeginInvoke’d, para que você possa terminar o thread que estava executando anteriormente . Além disso, se eu continuei o AsyncCallback, não há nenhum método naqueles AsyncCallbacks que podem terminar adequadamente o thread de execução anterior.

EndInvoke não pode finalizar o segmento, ele ainda concluir a operação do segmento a ser finalizado. Eu ainda acabaria usando Abort () no thread.

Então eu decidi apenas implementá-lo com abordagem de thread puro, sem o AsyncCallback. Isto é thread.abort () normal e seguro para você?

public delegate DataSet LookupValuesDelegate(LookupTextEventArgs e); internal delegate void PassDataSet(DataSet ds); public class AutoCompleteBox : UserControl { Thread _yarn = null; [System.ComponentModel.Category("Data")] public LookupValuesDelegate LookupValuesDelegate { set; get; } void DataSetCallback(DataSet ds) { if (this.InvokeRequired) this.Invoke(new PassDataSet(DataSetCallback), ds); else { // implements the appending of text on textbox here } } private void txt_TextChanged(object sender, EventArgs e) { if (_yarn != null) _yarn.Abort(); _yarn = new Thread( new Mate { LookupValuesDelegate = this.LookupValuesDelegate, LookupTextEventArgs = new LookupTextEventArgs { RowOffset = offset, Filter = txt.Text }, PassDataSet = this.DataSetCallback }.DoWork); _yarn.Start(); } } internal class Mate { internal LookupTextEventArgs LookupTextEventArgs = null; internal LookupValuesDelegate LookupValuesDelegate = null; internal PassDataSet PassDataSet = null; object o = new object(); internal void DoWork() { lock (o) { // the actual code that queries the database var ds = LookupValuesDelegate(LookupTextEventArgs); PassDataSet(ds); } } } 

NOTAS

O motivo para cancelar o thread anterior quando o usuário digita as chaves em sucessão, não é apenas para impedir que o acréscimo de texto ocorra, mas também para cancelar a ida e volta da rede anterior, para que o programa não esteja consumindo muita memory resultante de sucessivas operação de rede.

Estou preocupado se eu evitar Thread.Abort () completamente, o programa poderia consumir muita memory.

aqui está o código sem o thread.Abort (), usando um contador:

 internal delegate void PassDataSet(DataSet ds, int keyIndex); public class AutoCompleteBox : UserControl { [System.ComponentModel.Category("Data")] public LookupValuesDelegate LookupValuesDelegate { set; get; } static int _currentKeyIndex = 0; void DataSetCallback(DataSet ds, int keyIndex) { if (this.InvokeRequired) this.Invoke(new PassDataSet(DataSetCallback), ds, keyIndex); else { // ignore the returned DataSet if (keyIndex < _currentKeyIndex) return; // implements the appending of text on textbox here... } } private void txt_TextChanged(object sender, EventArgs e) { Interlocked.Increment(ref _currentKeyIndex); var yarn = new Thread( new Mate { KeyIndex = _currentKeyIndex, LookupValuesDelegate = this.LookupValuesDelegate, LookupTextEventArgs = new LookupTextEventArgs { RowOffset = offset, Filter = txt.Text }, PassDataSet = this.DataSetCallback }.DoWork); yarn.Start(); } } internal class Mate { internal int KeyIndex; internal LookupTextEventArgs LookupTextEventArgs = null; internal LookupValuesDelegate LookupValuesDelegate = null; internal PassDataSet PassDataSet = null; object o = new object(); internal void DoWork() { lock (o) { // the actual code that queries the database var ds = LookupValuesDelegate(LookupTextEventArgs); PassDataSet(ds, KeyIndex); } } } 

Não, não é seguro. Thread.Abort() é bastante delineado no melhor dos tempos, mas nesse caso seu controle não tem (heh) controle sobre o que está sendo feito no retorno de chamada delegado. Você não sabe em que estado o restante do aplicativo ficará e pode se encontrar em um mundo de dor quando chegar a hora de ligar para o delegado novamente.

Configurar um timer Espere um pouco após a alteração do texto antes de chamar o delegado. Depois espere que ele retorne antes de chamá-lo novamente. Se for lento, ou se o usuário estiver digitando tão rápido, provavelmente não esperará o autocomplete.

Em relação ao seu código atualizado (Abort () – free):

Agora você está lançando um novo segmento para (potencialmente) cada pressionamento de tecla . Isso não só vai matar desempenho, é desnecessário – se o usuário não está pausando, eles provavelmente não estão procurando pelo controle para completar o que estão digitando.

Eu falei sobre isso antes, mas P Daddy disse melhor :

Você seria melhor apenas implementar um timer one-shot, com talvez um tempo limite de meio segundo, e redefinir em cada pressionamento de tecla.

Pense nisso: um typescriptr rápido pode criar uma pontuação de threads antes que o primeiro callback do autocomplete tenha a chance de ser concluído, mesmo com uma conexão rápida com um database rápido. Mas se você atrasar a solicitação até um curto período de tempo após o último pressionamento de tecla, você terá uma chance melhor de atingir o ponto ideal em que o usuário digitou tudo o que eles querem (ou todos eles sabem!) E é apenas começar a esperar pelo autocomplete para chutar. Jogar com o atraso – meio segundo pode ser apropriado para os impacientes datilógrafos, mas se os seus usuários estão um pouco mais relaxados … ou o seu database é um pouco mais lento … então você pode obter melhores resultados com um atraso de 2-3 segundos ou até mais. A parte mais importante desta técnica, no entanto, é que você reset the timer on every keystroke .

E, a menos que você espere que as solicitações do database sejam realmente interrompidas , não se preocupe em tentar permitir várias solicitações simultâneas. Se uma solicitação estiver em andamento, aguarde a conclusão antes de fazer outra.

Há muitos avisos em toda a net sobre o uso de Thread.Abort . Eu recomendaria evitá-lo a menos que seja realmente necessário, o que, neste caso, não acho que seja. Você seria melhor apenas implementar um timer one-shot, com talvez um tempo limite de meio segundo, e redefinir em cada pressionamento de tecla. Dessa forma, sua operação cara só ocorre após meio segundo ou mais (ou qualquer tamanho que você escolha) de inatividade do usuário.

Você pode querer dar uma olhada em Introdução à Programação com Threads C # – Andrew D. Birrell. Ele descreve algumas das melhores práticas em torno do encadeamento em C #.

Na página 4 ele diz:

Quando você olha para o namespace “System.Threading”, você (ou deve) se sentir intimidado pelo leque de opções que enfrenta: “Monitor” ou “Mutex”; “Wait” ou “AutoResetEvent”; “Interromper” ou “Abortar”? Felizmente, há uma resposta simples: use a instrução “lock”, a class “Monitor” e o método “Interrupt”. Esses são os resources que eu usarei para a maior parte do restante do trabalho. Por enquanto, você deve ignorar o resto de “System.Threading”, embora eu possa descrevê-lo para a seção 9.

Não, eu evitaria sempre chamar o Thread.Abort no seu próprio código. Você quer que seu próprio segmento de plano de fundo seja concluído normalmente e desenrole sua pilha naturalmente. A única vez que eu consideraria chamar Thread.Abort é em um cenário onde meu código está hospedando o código externo em outro segmento (como um cenário de plugin) e eu realmente quero abortar o código externo.

Em vez disso, nesse caso, você pode considerar simplesmente fazer o version control de cada solicitação em segundo plano. No retorno de chamada, ignore as respostas que estão “desatualizadas”, já que as respostas do servidor podem retornar na ordem errada. Eu não me preocuparia muito em abortar uma solicitação que já foi enviada para o database. Se você achar que seu database não é responsivo ou está sobrecarregado por muitas solicitações, considere também usar um cronômetro como outros sugeriram.

Use Thread.Abort apenas como uma medida de último recurso quando estiver saindo do aplicativo e SAIBA que todos os resources IMPORTANTES são liberados com segurança.

Caso contrário, não faça isso. É ainda pior então

 try { // do stuff } catch { } // gulp the exception, don't do anything about it 

internet Segura…