Resolução de problemas de aplicações do ASP.NET com a utilização de palavras-chave estática

Coluna de voz de suporte do ASP.NET

Resolução de problemas de aplicações do ASP.NET com a utilização de palavras-chave estática

Para personalizar esta coluna às suas necessidades, pretendemos convidá-lo para apresentar as suas ideias sobre os tópicos que interessam problemas que pretende ver e corrigida no futuro colunas de voz de suporte e artigos da Base de dados de conhecimento. Pode submeter as ideias e comentários utilizando o formulário Pedir para-lo . Existe também uma hiperligação para o formulário na parte inferior desta coluna.
Bem-vindo à coluna de voz de suporte do Microsoft ASP.NET! O meu nome estiver Jerry Orman. Ter sido com a Microsoft 5 anos e ter gasto a maior parte do meu centrado sobre tecnologias relacionadas com a Web tal como o Microsoft FrontPage e novas tecnologias Microsoft SharePoint. Ter gasto a ano passado a trabalhar com o Microsoft ASP.NET como um engenheiro de suporte. Este mês na coluna voz de suporte, vou para descrever aplicações do ASP.NET resolução de problemas com a utilização de palavras-chave estática.

Statics

Este artigo descreve dois diferentes comportamentos que são causadas por palavras-chave estática e que podem ser difíceis de resolver problemas. Espera-se grande Depois de ler este artigo, pode evitar estes comportamentos nas suas aplicações e conseguir melhor diagnosticá-los se estes ocorrerem.

Os utilizadores vêem dados incorrectos

Um sintoma que é muito difícil resolver ocorre quando um utilizador submete dados, mas são apresentadas ou são introduzidas numa base de dados de informações de outro utilizador. Este comportamento ocorre normalmente devido a utilização incorrecta de variáveis estáticas em classes. Um objecto estático é instanciada uma vez para cada domínio de aplicação e todo o processamento da aplicação partilha o mesmo objecto. Por exemplo, vamos supor que configura uma propriedade numa classe estática, utilizando o seguinte código.
using System;
namespace statics
{
public class BadClass
{
private static string _MyData;

public static string MyData
{
get{return _MyData;}
set{_MyData = value;}
}
}
}

A propriedade de meusdados é estático e público. Pode acedê-lo em qualquer local na sua aplicação utilizando a sintaxe seguinte:

BadClass.MyData

Agora imagine que tem uma página que define o valor do meusdadose, em seguida, alguns funciona com o mesmo. Se vários utilizadores a página de acesso ao mesmo tempo, tem agora uma condição de corrida em que o último utilizador que actualizou
Wins meusdados . O exemplo seguinte mostra este comportamento, definindo o objecto estático durante um evento button_click . Este exemplo também torna a suspensão de thread durante 15 segundos. Isto permite que os pedidos ser executada ao mesmo tempo. Para tal, siga estes passos:
  1. Crie um formulário Web do. aspx.
  2. Adicione um rótulo, um botão e uma caixa de texto. O código HTML será semelhante à seguinte.
    <body><form id="Form1" method="post" runat="server">
    <asp:Label id="Label1" runat="server"></asp:Label>
    <br>
    <asp:Button id="Button1" runat="server" Text="Button"></asp:Button>
    </br>
    <asp:TextBox id="TextBox1" runat="server"></asp:TextBox>
    </form>
    </body>


  3. Faça duplo clique no botão para criar um evento button_click .
    private void Button1_Click(object sender, System.EventArgs e){
    //Set the static property to the value of the text box.
    BadClass.MyData = TextBox1.Text;

    //Make the thread wait 30 seconds
    System.Threading.Thread.Sleep(30000);

    //Set the label to the value of the property.
    Label1.Text = BadClass.MyData;
    }
  4. Abrir separado dois processos Microsoft Internet Explorer:
    1. Clique em Iniciar, aponte para Todos os programase, em seguida, clique em Internet Explorer.
    2. Repita o passo um.
    Isto irá garantir que tem dois processos separados de Iexplore.exe ao mesmo tempo a ser executado. Pode verificar se o Gestor de tarefas para se certificar. Quando tiver o dois de Iexplore.exe de processos em execução ao mesmo tempo, tem sessões exclusivos para cada pedido de página. Os pedidos que têm o mesmo ID de sessão são processados pela ordem em que estes entram e não são executadas ao mesmo tempo.
  5. Procure a página. aspx em ambas as janelas do browser.
  6. Submeter Utilizador1 na caixa de texto de uma janela do browser e, em seguida, submeter Utilizador2 na caixa de texto na outra janela do browser.

    Depois de concluir as páginas, verá
    Utilizador2 em ambos os rótulos.
Conforme mencionado anteriormente, ambas as páginas estão em execução ao mesmo tempo. O primeiro pedido define a propriedade estática para o Utilizador1 e, em seguida, entra no modo de suspensão. Enquanto o thread está em pausa, outro pedido chega e define o mesmo objecto Utilizador2. O thread que está a processar o primeiro pedido estiver concluída e, em seguida, apresenta o valor. O valor é definido como utilizador2.

Evitar este comportamento

A melhor forma para evitar este comportamento é para tornar o objecto não estático e utilizar uma instância da classe para definir e obter os valores. A propriedade já não é partilhada, uma vez que é criada uma cópia do objecto para cada pedido e não verá este comportamento. Para evitar este comportamento, siga estes passos:
  1. Remova o modificador estático do ficheiro BadClass.cs.
    using System;
    namespace statics
    {
    public class BadClass
    {
    private string _MyData; //Removed static

    public string MyData//Removed static
    {
    get{return _MyData;}
    set{_MyData = value;}
    }
    }
    }

  2. Altere o evento button_click .
    private void Button1_Click(object sender, System.EventArgs e){
    //Create an instance of BadClass
    BadClass _bad = new BadClass();

    //Set the MyData property on this instance
    _bad.MyData = TextBox1.Text;

    //Sleep for 30 seconds
    System.Threading.Thread.Sleep(30000);

    //Set the label to the value of the property
    Label1.Text = _bad.MyData;
    }

Se tiver de utilizar o objecto estático, tem de implementar o bloqueio quando estiver a utilizar o objecto estático. Isto impede que os dois threads aceder ao objecto ao mesmo tempo. Para tal, siga estes passos:
  1. Adicione um objecto estático para o código subjacente a página. Este objecto será que as páginas podem obter um bloqueio num recurso partilhado.
    static object StaticLocker = new object();
  2. Altere o evento button_click para o seguinte.
    private void Button1_Click(object sender, System.EventArgs e){
    //Lock the object to prevent multiple threads from writing to the static property
    lock(StaticLocker)
    {
    //Set the static property
    BadClass.MyData = TextBox1.Text;

    //Put the thread to sleep for 30 seconds
    System.Threading.Thread.Sleep(30000);

    //Set the label to the value of the static property
    Label1.Text = BadClass.MyData;
    }
    }

NullReferenceException causado por statics

Outro comportamento que ocorre quando utiliza o statics é uma excepção de System. NullReferenceException. Por exemplo, alterar o código de evento button_click à seguinte.
private void Button1_Click(object sender, System.EventArgs e){
BadClass.MyData = TextBox1.Text;
System.Threading.Thread.Sleep(10000);
Label1.Text = BadClass.MyData.ToString();
BadClass.MyData = null;
}

Se efectuar o mesmo teste como antes, quando BadClass.MyData.ToString() é chamado para o segundo pedido, este será accione uma excepção de System. NullReferenceException. A excepção de System. NullReferenceException ocorre porque o primeiro pedido defina o valor como nulo enquanto o segundo pedido esteve à espera. A resolução para este comportamento é a mesma que para uma propriedade estática.
Fuga de memória causada por acontecimentos estáticos
Se configurar um evento estático e, em seguida, subscrever esse evento de uma página. aspx, o processo, eventualmente, ficará sem memória disponível. Vamos considerar que adicione o seguinte no ficheiro BadClass.cs.
public delegate void MyEvent();public static event MyEvent BadEvent;

É, em seguida, do fio do delegado do evento no método InitializeComponent da página. aspx para um método na página.
private void InitializeComponent(){    
//Add this with the other items (for example, this.Load).
BadClass.BadEvent += new BadClass.MyEvent(this.TestEvent);
}

Também é adicionar o seguinte método para a página. Este é o método que especificou quando ao ligar o evento no método InitializeComponent :
private void TestEvent(){
//Do some work here.
}

Porque é que esta situação provoca uma fuga de memória
Uma vez que o evento é estático e nunca sai fora do âmbito, está a adicionar o método à lista de eventos que são accionados quando o evento ocorre sempre que a página é executada. O resultado final é que qualquer que seja objecto de fio para o evento estático tem raiz na memória, e nunca serão recolhido. Neste caso, esse objecto é a classe de página. aspx real.

Este comportamento ocorre, o depurador de Windbg.exe ou o depurador de Cdb.exe, executando o ! gcroot comando no objecto que está a adicionar o evento. Poderá ver o resultado é semelhante à seguinte.
HANDLE(Strong):c3d11d8:Root:0x90b8838(System.Object[])->0x5346f68(statics.BadClass/MyEvent)->0x10ff928(statics.BadClass/MyEvent)->
0x10faa24(statics.BadClass/MyEvent)->

Número de objecto de pedidos tenha sido efectuada aos seguintes eventos.
0x10e6fe0(statics.BadClass/MyEvent)->0x533baf0(ASP.StaticsTest_aspx)
Pode ver que o item inferior é a classe de página. aspx e raiz para múltiplos delegados de MyEvent. Uma vez que a página. aspx é uma raiz, não é limpo. O outro efeito secundário é que tudo o que é utilizando a página. aspx também não pode ser limpo porque eles são com raiz no objecto de página.

Para mais informações sobre o recolector de lixo de .NET da Microsoft, visite os seguintes Web sites da MSDN:Para mais informações sobre o ! gcroot comando e obter este resultado, consulte a secção de "Contadores de memória do .NET CLR" do seguinte Web site da MSDN:

Evitar este comportamento

Para evitar este comportamento, pode não utilizar a palavra-chave estática no evento ou remover o processador de eventos da página quando tiver terminado a utilizá-lo. Um exemplo de ASP.NET, fio o evento, quando é chamado Page_Init . Tem de removê-lo quando carrega a página adicionando um processador de eventos para o evento Page_Unload .

Adicione o seguinte para o método InitializeComponents .
this.Unload += new System.EventHandler(this.Page_Unload);
Add the following method to the page to remove the event:
private void Page_Unload(object sender, System.EventArgs e)
{
BadClass.test -= new BadClass.MyEvent(this.TestEvent);
}

Para mais informações sobre eventos, visite o seguinte Web site da MSDN:
Como sempre, sensação gratuita submeter ideias sobre os tópicos que pretende no futuro corrigida colunas ou na Knowledge Base utilizando o
Pedir para este formulário.
Propriedades

ID do Artigo: 893666 - Última Revisão: 21/02/2017 - Revisão: 1

Comentários