threads thread for book c# multithreading memory-leaks event-handling media-player

for - multithreading c# book



Abrir MediaPlayer-evento no administrado en Thread (1)

Intento crear un reproductor multimedia (con la clase Media.MediaPlayer ()) y para esto estoy usando un hilo para procesar las canciones que el usuario carga usando OpenFileDialog. Estoy usando el siguiente código para comenzar el proceso de las canciones:

public static List<MediaFile> MediaList = new List<MediaFile>(); public static Queue<String> MediaFilesQueue = new Queue<String>(); public static void AddMediaFilesToMediaList() { String pathToFile; while (MediaFilesQueue.Count > 0) // all the files are loaded into the Queue before processing { pathToFile = MediaFilesQueue.Dequeue(); MediaData.MediaList.Add(new MediaFile(pathToFile)); MediaFileCreator mfCreator = new MediaFileCreator(MediaData.MediaList.Count - 1); mfCreator.CreateNewMediaFile(); }

}

Y esta es la clase MediaFileCreator:

public class MediaFileCreator { private int IndexOfMediaFileCurrentlyProcessed; public MediaFileCreator(int idx) { IndexOfMediaFileCurrentlyProcessed = idx; } public void CreateNewMediaFile() { var indexOfMediaFileCurrentlyProcessed = IndexOfMediaFileCurrentlyProcessed; var tempMediaFile = MediaData.MediaList[indexOfMediaFileCurrentlyProcessed]; var tempMediaPlayer = new MediaPlayer(); var waitHandle = new AutoResetEvent(false); //EventHandler eventHandler = delegate(object sender, EventArgs args) //{ // waitHandle.Set(); //}; tempMediaPlayer.MediaOpened += (sender, args) => waitHandle.Set(); tempMediaPlayer.Open(new Uri(tempMediaFile.PathToFile)); waitHandle.WaitOne(); //while (!tempMediaPlayer.NaturalDuration.HasTimeSpan) //{ // Thread.Sleep(100); //} var tempTimeSpan = tempMediaPlayer.NaturalDuration.TimeSpan; var hasVideo = tempMediaPlayer.HasVideo; var hasAudio = tempMediaPlayer.HasAudio; MediaData.MediaList[indexOfMediaFileCurrentlyProcessed].HasVideo = hasVideo; MediaData.MediaList[indexOfMediaFileCurrentlyProcessed].HasAudio = hasAudio; MediaData.MediaList[indexOfMediaFileCurrentlyProcessed].TimeSpanOfMediaFile = tempTimeSpan; }

Y esta es la clase MediaFile:

public class MediaFile { public bool HasAudio = false; public bool HasVideo = false; public TimeSpan TimeSpanOfMediaFile; public String PathToFile = null; public MediaFile(String pathToFile) { PathToFile = pathToFile; } }

Mi problema es que el programa se detiene en waitHandle.WaitOne(); e intenta correr esa línea una y otra vez. Probé otras variantes como la de la primera sección comentada al adjuntar ese controlador de eventos al evento Open, pero el resultado fue el mismo: waitHandle.Set(); nunca se ejecuta y el valor de waitHandle siempre es falso. La única opción que logré que funcione es la solución de la segunda sección comentada: bloquear el hilo (con Thread.Sleep) hasta que el archivo esté completamente cargado (el archivo se carga, por ejemplo, cuando se inicializa el TimeSpan) ... que es tiempo perdido y disminución de rendimiento para mi aplicación. El problema obviamente no es con el evento en sí, porque el evento se desencadena si se ejecuta en el hilo principal (se llama al método AddMediaFilesToMediaList () desde un subproceso BackgroundWorker que inicia el método dentro de un nuevo subproceso cuando detecta que hay elementos en el Queue; el subproceso AddMediaFilesToMediaList () se crea con el new Thread() ) y obviamente el archivo se está cargando porque el TimeSpan está inicializado. Realmente quiero hacer que la aplicación funcione usando waitHandle o algo así. No quiero usar Thread.Sleep () porque es feo y mi aplicación también tiene una pérdida de memoria cuando intento cargar muchos archivos (tomo más de 1,2 GB de memoria y se detiene con errores (OutOfMemory) - Intenté cargar 2048 canciones en él) y creo que podría deberse a Thread.Sleep (). Incluso si no lo es, será mucho más fácil de depurar sin el problema del Thread.Sleep ().

Entonces, ¿cómo puedo hacer que waitHandle funcione? ¿Cómo puedo hacer el waitHandle.Set(); correr ? Y si alguien tiene alguna idea de dónde podría provenir ese uso excesivo de la memoria, ¡sería genial! (Personalmente creo que es culpa de Thread.Sleep () pero no sé cómo deshacerme de él).

Editar: El motivo por el que uso un objeto MediaFileCreator es que inicialmente quería usar un grupo de subprocesos de 2 a 4 subprocesos para procesar los archivos multimedia, pero tenía el mismo problema, así que eliminé el ThreadPool y lo intenté con el código publicado anteriormente. pero el mismo problema ocurrió.

Editar: logré que funcione usando un segundo hilo para esperar el evento (no es el código más limpio en este momento, pero lo haré bien).

public class MediaFileCreator { private AutoResetEvent openedEvent = new AutoResetEvent(false); public MediaFile CreateNewMediaFile(string filename) { var mFile = new MediaFile(filename); var thread = new Thread(WaitForEvent); const int maxTimeToWait = 2000; openedEvent.Reset(); thread.Start(mFile); var mediaPlayer = new MediaPlayer(); mediaPlayer.Open(new Uri(mFile.PathToFile)); openedEvent.WaitOne(maxTimeToWait); var fromThread = Dispatcher.FromThread(Thread.CurrentThread); if (fromThread != null) fromThread.InvokeShutdown(); return mFile; } private void WaitForEvent(object context) { var mFile = (MediaFile)context; var mediaPlayer = new MediaPlayer(); mediaPlayer.MediaOpened += delegate { if (mediaPlayer.NaturalDuration.HasTimeSpan) mFile.TimeSpanOfMediaFile = mediaPlayer.NaturalDuration.TimeSpan; mFile.HasAudio = mediaPlayer.HasAudio; mFile.HasVideo = mediaPlayer.HasVideo; mFile.Success = true; mediaPlayer.Close(); openedEvent.Set(); }; mediaPlayer.MediaFailed += delegate { mFile.Failure = true; mediaPlayer.Close(); openedEvent.Set(); }; mediaPlayer.Open(new Uri(mFile.PathToFile)); Dispatcher.Run(); } }


Una cosa es cierta: la llamada Thread.Sleep no tiene nada que ver con su problema de memoria.

Sugeriría que limpies tu código un poco más. No es necesario que cree un nuevo MediaCreator y un nuevo MediaPlayer por cada archivo que cargue. Esa podría ser una gran parte del uso de memoria, ya que está creando todos esos objetos MediaPlayer y no los está cerrando. El recolector de basura finalmente los limpiará, pero puede hacer que el uso de la memoria parezca enorme mientras tanto.

Considera esto:

public static void AddMediaFilesToMediaList() { MediaFileCreator mfCreator = new MediaFileCreator(); while (MediaFilesQueue.Count > 0) { // all the files are loaded into the Queue before processing string pathToFile = MediaFilesQueue.Dequeue(); MediaFile mf = mfCreator.CreateNewMediaFile(pathToFile); MediaData.MediaList.Add(mf); } } public class MediaFileCreator { private MediaPlayer player = new MediaPlayer(); private ManualResetEvent openedEvent = new ManualResetEvent(false); public MediaFileCreator() { player.MediaOpened = MediaOpened; } private void MediaOpened(object sender, EventArgs args) { openedEvent.Set(); } public MediaFile CreateNewMediaFile(string filename) { openedEvent.Reset(); player.Open(new Uri(tempMediaFile.PathToFile)); // wait for it to load openedEvent.WaitOne(); MediaFile mf = new MediaFile(filename); mf.HasVideo = player.HasVideo; mf.HasAudio = player.HasAudio; mf.TimeSpanOfMediaFile = player.NaturalDuration.TimeSpan; player.Close(); return mf; } }

Eso simplifica bastante las cosas y usa solo un objeto MediaPlayer , lo que debería reducir bastante el uso de la memoria.

Realmente no sé cuál es el problema con tu AutoResetEvent no funciona. Se ve bien

En este nuevo código, puede poner un punto de interrupción en la llamada waitHandle.Set para ver si realmente se golpeó. No sé exactamente MediaOpened se desencadena el evento MediaOpened , ni cuál es el estado del jugador cuando se produce el evento. La documentación guarda sospechosamente silencio al respecto.

Es posible que el problema sea que MediaPlayer quiere que su código se ejecute en el hilo de UI. No estoy lo suficientemente familiarizado con los controles WPF para decir. Puede llamar a VerifyAccess para determinar si su hilo tiene acceso al objeto.