我遇到的问题是我的网络应用程序经常崩溃并重置IIS中的应用程序池,导致严重的性能问题,以及擦除我的应用程序中运行的任何计时线程。
该站点是在AWS的2012 Windows Server EC2实例上运行的.NET 4.5.2 C#MVC5站点。
当我开始看到网站在运行这么多分钟后加载时,首先注意到这个问题。我认为它可能是ApplicationPool回收并确保在IIS中正确设置IdleTime和Application Preload。这个问题仍然存在。
接下来,我前往服务器管理器检查事件日志,发现这些条目大约每15分钟左右发生一次:
错误应用程序名称:w3wp.exe,版本:8.5.9600.16384,时间 标记:0x5215df96错误模块名称:WMNetMgr.dll_unloaded, 版本:12.0.9600.17415,时间戳:0x545047db异常代码: 0xc0000005故障偏移:0x00000000000cf5cf故障进程ID: 0x17d0错误应用程序启动时间:0x01d331dc20f096d0错误 应用程序路径:c:\ windows \ system32 \ inetsrv \ w3wp.exe故障模块 路径:WMNetMgr.dll报告ID:777a35de-9dd1-11e7-81d7-025ff0be916d 错误包全名:错误包相关应用程序ID:
和
WIN-7PCRJOFR05F 5011警告Microsoft-Windows-WAS系统9/20/2017 7:01:04 AM - 为应用程序池提供服务的流程' SiteName'遭遇了 Windows进程激活服务导致致命的通信错误。 流程ID为' 6096'数据字段包含错误编号。
接下来我运行了DebugDiag2 Collection and Analysis:
警告 - DebugDiag无法找到调试符号 WMNetMgr.dll>,因此以下信息可能不完整。 在 w3wp__SiteName__PID__5088__Date__09_20_2017__Time_06_31_02AM__436__Second_Chance_Exception_C0000005.dmp 当线程26发生访问冲突异常(0xC0000005)时 另一个模块试图调用以下卸载的模块: WMNetMgr.dll>
主题26: 调用堆栈Unloaded_WMNetMgr.dll + cf5cf 0x000000de
575cf7c0 0x000000dc
2ed5ec10
这是此调试器报告的唯一错误。报告中的.NET堆栈跟踪中没有其他例外。我似乎无法获得此特定.dll的调试符号,这些消息似乎没有用处。
该应用程序利用WMPLib在wmplayer启动时创建单例实例,以通过来自客户端的Web请求在Windows Server 2012实例上播放声音。该应用程序在这方面起作用,没有问题播放声音和来自多个用户的请求。
这是Singleton:
public sealed class SoundboardSingleton : IDisposable
{
private static readonly Lazy<SoundboardSingleton> lazy =
new Lazy<SoundboardSingleton>(() => new SoundboardSingleton());
public static SoundboardSingleton Instance { get { return lazy.Value; } }
public WindowsMediaPlayer WMPlayer;
private StayAliveBot _liveBot;
private Thread _botThread;
private SoundboardSingleton()
{
WMPlayer = new WindowsMediaPlayer();
WMPlayer.settings.volume = 50;
_liveBot = new StayAliveBot();
_botThread = new Thread(_liveBot.Live);
_botThread.Start();
}
public void Dispose()
{
if (_botThread.IsAlive)
{
_botThread.Abort();
}
}
}
public class StayAliveBot
{
public void Live()
{
while (SoundboardSingleton.Instance != null)
{
Thread.Sleep(1500000);
SoundboardHelper.PlaySound("C:\\SoundboardOpFiles\\TestTone.wav");
}
}
}
最初通过以下方式在Startup.cs中实例化:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
// startup soundboard singleton
SoundboardSingleton.Instance.WMPlayer.settings.volume = 50;
}
}
我可以在我的本地开发机器上运行此应用程序,没有任何问题或崩溃。一切都按预期运行,没有崩溃。在部署到EC2实例时,站点上的所有内容都能正常运行,但现在每15分钟就会崩溃/重置一次。
我怀疑是:
A)WMPLib实例存在问题,并且在Windows Server 2012框中缺少一些依赖性,允许它播放声音,但会导致定期崩溃。
B)我在单例实例化时犯了一个错误,它以某种方式崩溃了我的应用程序。
我尝试过解决方案here,但没有结果。
任何帮助都将不胜感激。
编辑:我已经确认该问题与WMPLib的使用有关,因为删除它的使用每15分钟就会停止崩溃。仍然不确定为什么会这样。
答案 0 :(得分:2)
这不是对你的问题的直接回答,而是对同一件事采用不同的方式。而不是WMPLib COM控件,尝试使用WPF中的线程安全的MediaPlayer类。添加对WindowsBase和PresentationCore的引用,并使用类似的东西:
using System.Windows.Media;
public void PlaySound(string filename)
{
var mplayer = new MediaPlayer();
mplayer.MediaEnded += new EventHandler(MediaEndedHandler);
mplayer.Open(new Uri(filename));
mplayer.Play();
}
public void MediaEndedHandler(object sender, EventArgs e)
{
((MediaPlayer)sender).Close();
}
您也可以像上面一样使用它作为单例,并且它是完全线程安全的,而WMPLib则不是。
修改强>
如评论中所述,您实际上只能使用具有公共bool属性的静态类来显示忙碌信号。 IIS中的静态类在应用程序的所有请求之间共享,并且该类仅在应用程序池被回收时进行垃圾收集,因此您需要小心存储在其中的对象的生命周期,以避免内存消费问题。此代码将为每个PlaySound()使用媒体播放器类的新实例,并在完成播放后立即将其处理掉,但忙标志在对服务器发出的所有请求中都很常见。
using System;
using System.Threading;
using System.Windows.Media;
namespace SoundBoardApp
{
public static class Soundboard
{
private static bool _isBusy = false;
public static bool IsBusy { get { return _isBusy; } }
private static void MediaEndedHandler(object sender, EventArgs e)
{
_isBusy = false;
var wmp = ((MediaPlayer)sender);
wmp.MediaEnded -= new EventHandler(MediaEndedHandler);
wmp.Close();
}
public static bool PlaySound(string filename)
{
if (!_isBusy)
{
_isBusy = true;
var wmp = new MediaPlayer();
wmp.MediaEnded += new EventHandler(MediaEndedHandler);
wmp.Volume = 0.5;
wmp.Open(new Uri(filename));
wmp.Play();
return true;
}
else
{
return false;
}
}
}
public class StayAliveBot
{
public void Live()
{
while (true)
{
Thread.Sleep(1500000);
if (!Soundboard.IsBusy) Soundboard.PlaySound("C:\\SoundboardOpFiles\\TestTone.wav");
}
}
}
}
答案 1 :(得分:1)
我最终使用NAudio和我的单身模式。
根据Lex Li的建议,我使用第三方作为Windows.MediaPlayer不适用于网络应用程序。根据Drunken Code Monkey的解决方案,我在单例上使用布尔标志来评估由单独的线程经常检查的播放状态,该线程评估我的单例中IWavePlayer对象上的PlaybackState.Stopped值。我唯一担心的是表现。我还没有注意到任何问题,但我非常确定如果可以通过网络应用程序进行操作,那么在Handler中管理事件会更高效,代码更少。< / p>
以下是代码:
using NAudio.Wave;
public sealed class SoundboardSingleton : IDisposable
{
private static readonly Lazy<SoundboardSingleton> lazy =
new Lazy<SoundboardSingleton>(() => new SoundboardSingleton());
public static SoundboardSingleton Instance { get { return lazy.Value; } }
public IWavePlayer WaveOutDevice { get; set; }
public AudioFileReader AudioFileReaderObj { get; set; }
public float Volume { get; set; }
private MediaCloser _mediaCloser;
private Thread _mediaCloserThread;
private StayAliveBot _liveBot;
private Thread _botThread;
public bool IsBusy { get; set; }
private SoundboardSingleton()
{
// checks our NAudio WaveOutDevice playback for stop
_mediaCloser = new MediaCloser();
_mediaCloserThread = new Thread(_mediaCloser.CheckForStoppedPlayback);
_mediaCloserThread.Start();
// thread to play sound every 25 minutes, to avoid idle flag
_liveBot = new StayAliveBot();
_botThread = new Thread(_liveBot.Live);
_botThread.Start();
}
public bool PlaySound(string filename)
{
// make sure we are not active
if (IsBusy) { return false; }
// process sound
IsBusy = true;
WaveOutDevice = new WaveOutEvent();
AudioFileReaderObj = new AudioFileReader(filename);
AudioFileReaderObj.Volume = Volume;
WaveOutDevice.Init(AudioFileReaderObj);
WaveOutDevice.Play();
return true;
}
public void CloseWaveOut()
{
// clean up sound objects
WaveOutDevice?.Stop();
if (AudioFileReaderObj != null)
{
AudioFileReaderObj.Dispose();
AudioFileReaderObj = null;
}
if (WaveOutDevice != null)
{
WaveOutDevice.Dispose();
WaveOutDevice = null;
}
}
public void Dispose()
{
if (_mediaCloserThread.IsAlive)
{
_mediaCloserThread.Abort();
}
if (_botThread.IsAlive)
{
_botThread.Abort();
}
}
}
public class MediaCloser
{
public void CheckForStoppedPlayback()
{
while (true)
{
// continuously check for our stopped playback state to cleanup
Thread.Sleep(500);
if (SoundboardSingleton.Instance.WaveOutDevice != null &&
SoundboardSingleton.Instance.WaveOutDevice.PlaybackState == PlaybackState.Stopped)
{
SoundboardSingleton.Instance.CloseWaveOut();
SoundboardSingleton.Instance.IsBusy = false;
}
}
}
}
public class StayAliveBot
{
public void Live()
{
while (true)
{
// prevent bot from going idle
Thread.Sleep(1500000);
if (!SoundboardSingleton.Instance.IsBusy)
{
SoundboardSingleton.Instance.PlaySound(ConfigurationManager.AppSettings["SoundboardHeartbeatFile"]);
}
}
}
}
希望这可以帮助任何人遇到同样的问题。我的网站已经启动并运行了几个小时,没有任何问题,客户向董事会发送垃圾邮件。再次感谢所有帮助过的人。