c# - ¿Podría un método BeginInvokeOnMainThread ser repetitivo y causar una pérdida de memoria?
xamarin xamarin.forms (2)
En primer lugar, si le preocupa una pérdida de memoria, puede verificar las advertencias de poca memoria en los registros del dispositivo (accesibles a través de XCode) o anular el método ReceiveMemoryWarning en el delegado de su aplicación para registrar un error.
En segundo lugar, no hay nada obviamente incorrecto con la forma en que está llamando a BeginInvokeOnMainThread que podría causar una fuga. El ContinueWith es una operación no operativa que no afecta el funcionamiento del código; supongo que está ahí para evitar una advertencia del compilador de que no estás esperando la tarea.
En tercer lugar, si sospecha que este código está causando una fuga, debe usar el registro y / o puntos de interrupción para confirmar que se está comportando como se esperaba. ¿La tarea se cancela correctamente cuando navega fuera de la página? ¿Ve varias instancias de la tarea ShowCards ejecutándose? Si este código se comporta correctamente, la fuente del hang se encuentra en otra parte de su aplicación. Por ejemplo, parece que estás haciendo una llamada a la base de datos dos veces por segundo, tal vez no está limpiando los recursos correctamente.
Tengo una aplicación que funciona, pero después de un tiempo cuando depuro en mi iPhone cuelga el teléfono y la única forma en que puedo recuperar es un restablecimiento completo del botón en el lateral y el botón de inicio.
Antes que nada, ¿podría ser porque mi aplicación tiene una pérdida de memoria?
Aquí está el código para la aplicación. En particular, estoy mirando el método BeginInvokeOnMainThread
. ¿Puede alguien decirme si puede ver si puede haber algún problema con la forma en que se implementa? Además, ¿cuál es el propósito de .ContinueWith((arg)
.
namespace Japanese
{
public partial class PhrasesFrame : Frame
{
CancellationTokenSource cts = new CancellationTokenSource();
public PhrasesFrame(PhrasesPage phrasesPage)
{
InitializeComponent();
this.phrasesPage = phrasesPage;
AS.phrasesFrame = this;
Device.BeginInvokeOnMainThread(() => ShowCards(cts.Token).ContinueWith((arg) => { }));
}
public void Disappearing()
{
cts.Cancel();
}
public async Task ShowCards(CancellationToken ct)
{
AS.cardCountForSelectedCategories = App.DB.GetCardCountForSelectedCategories();
while (!ct.IsCancellationRequested)
{
await Task.Delay(500);
}
}
}
}
Continua con
Primero, .ContinueWith((arg) => { }))
su pregunta sobre .ContinueWith((arg) => { }))
. ContinueWith
más código para ejecutar una vez que la Task
original se haya completado. En nuestro caso, el código dentro de ContinueWith
se ejecutará una vez que Device.BeginInvokeOnMainThread(() => ShowCards(cts.Token)
haya finalizado.
En este caso, no hay código dentro de ContinueWith
, por lo que podemos eliminarlo.
Congelación
Sí, puedo ver que este código tiene el potencial de congelar la IU.
BeginInvokeOnMainThread
pondrá en cola una Action
para ejecutar en el subproceso principal (también conocido como subproceso de interfaz de usuario). El hilo principal está constantemente escuchando para la entrada del usuario (tocando un botón en la pantalla, pellizcar para acercar, etc.), y si este hilo está ocupado haciendo una tarea de larga ejecución, no podrá responder a un usuario entrada hasta que haya terminado; por lo tanto, su aplicación aparecerá congelada.
El código está a la await Task.Delay(500);
está siendo llamado por el hilo principal. Por lo tanto, le estamos diciendo al hilo principal que se congele durante 500 milisegundos, y lo repita indefinidamente.
Una solución sería envolver este código en Task.Run
que lo pondría en un hilo de fondo y liberaría el hilo principal para escuchar / responder a la entrada del usuario.
Task.Run(async () =>
{
while (!ct.IsCancellationRequested)
{
await Task.Delay(500);
}
}
Más recomendaciones de enhebrado
Solo use
BeginInvokeOnMainThread
cuando necesite actualizar la UI. El 99% del código se puede ejecutar en un hilo de fondo sin problemas. El 1%, sin embargo, es un código que actualiza la interfaz de usuario; cualquier código que actualice la UI se debe ejecutar en el hilo principal.Si una tarea que lleva más tiempo que la frecuencia de actualización de la pantalla se ejecutará, actívela en una cadena de fondo. Por ejemplo, si la frecuencia de actualización de la pantalla es de 60Hz, se actualiza 60 veces por segundo, cada 16.7ms. Entonces, si tenemos un bloque de código que tarda 20 ms en ejecutarse, debemos ejecutarlo en un hilo de fondo para asegurarnos de no congelar la aplicación y soltar ningún marco.
- El código anterior parece que está accediendo a una base de datos, que recomiendo encarecidamente mover a un hilo de fondo como lo hace
await Task.Run(() => AS.cardCountForSelectedCategories = App.DB.GetCardCountForSelectedCategories());