我正在尝试创建一个媒体播放器(使用Media.MediaPlayer()类),为此我使用一个线程来处理用户使用OpenFileDialog加载的歌曲。我正在使用下一个代码来启动歌曲的过程:
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();
}
}
这是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;
}
这是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;
}
}
我的问题是程序在waitHandle.WaitOne();
处停止并尝试一次又一次地运行该行。我通过将该事件处理程序附加到Open事件尝试了其他变体(如第一个注释部分中的变体),但结果是相同的:waitHandle.Set();
从不执行,而waitHandle的值始终为false。我设法工作的唯一选项是第二个注释部分的解决方案:阻塞线程(使用Thread.Sleep)直到文件完全加载(文件加载,例如初始化TimeSpan时)...这是我的申请时间丢失和性能下降。问题显然不在于事件本身,因为如果事件在主线程上运行,事件就会触发(从BackgroundWorker线程调用方法AddMediaFilesToMediaList(),当它检测到新元素中有元素时,会在新线程内启动该方法。队列; AddMediaFilesToMediaList()线程是使用new Thread()
创建的,显然文件正在加载,因为TimeSpan已初始化。我真的想使用waitHandle或类似的东西使应用程序工作。我不想使用Thread.Sleep(),因为它很难看,当我尝试加载大量文件时,我的应用程序也有内存泄漏(我需要超过1.2 GB的内存,并且它会因错误而停止(OutOfMemory) - 我试图在其中加载2048首歌曲)我相信它可能是因为Thread.Sleep()。即使不是,也可以在没有Thread.Sleep()问题的情况下进行调试。
那么如何让waitHandle工作呢?如何让waitHandle.Set();
运行?如果有人知道过多的内存使用可能来自何处,那就太棒了! (我个人认为这是Thread.Sleep()的错,但我不知道如何摆脱它)。
编辑:我设法让它使用第二个线程来等待事件(现在不是最干净的代码,但我会做对的)。
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();
}
}
答案 0 :(得分:3)
有一件事是肯定的:Thread.Sleep
电话与你的记忆问题无关。
我建议您稍微清理一下代码。您无需为加载的每个文件创建新的MediaCreator
和新的MediaPlayer
。这可能是内存使用的很大一部分,因为您正在创建所有这些MediaPlayer
对象而不是关闭它们。垃圾收集器最终将清理它们,但它可以使你的内存使用看起来很大。
考虑一下:
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;
}
}
这简化了一些事情并只使用了一个MediaPlayer
对象,这应该会减少内存使用量。
我真的不知道你的AutoResetEvent
无效的问题是什么。 看起来正确。
在这个新代码中,您可以在waitHandle.Set
调用上设置一个断点,看看它是否真的被击中了。我不知道什么时候触发了MediaOpened
事件,或者事件被引发时玩家的状态是什么。该文件对此持怀疑态度。
问题可能是MediaPlayer
希望其代码在UI线程上执行。我对WPF控件说不够熟悉。您可以调用VerifyAccess
来确定您的线程是否可以访问该对象。