即使应用程序池被回收,也可以执行保证在IIS网站中执行的异步操作

Dre*_*kes 8 .net iis asynchronous threadpool

我的网站用户执行的某些操作会导致发送电子邮件.发送电子邮件的代码可能会阻塞一段时间,所以我想从他们的HTTP请求处理程序的线程中执行此操作.

目前我正在使用类似的东西:

ThreadPool.QueueUserWorkItem(o => {
    try
    {
        email.Send();
    }
    catch (Exception ex)
    {
        _log.Error("Error sending email", ex);
    }
});
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,这是有效的.但是,该网站在托管环境中运行,可以回收应用程序池.

每隔一段时间我都不会收到应该发送的电子邮件,我怀疑在应用程序池回收期间,线程池队列中的这个工作项被删除了.

如何执行这样的ansync操作并保证在这种情况下完成?

Mar*_*Zen 8

如果应用程序以集成模式运行,则可以在主机环境中注册邮件分派器服务.主机将回收完成之前通知您的服务.它将执行2次调用IRegisteredObject.Stop.在第一次通话时,主持人为您提供完成工作的机会.如果达到超时且您的服务未从主机上移除,则进行另一次呼叫,但这次仅通知在有或没有服务同意的情况下进行回收.

这是一个如何实现Stop()方法的示例(未测试):

public class MailDispatchService : IRegisteredObject
{
    private AutoResetEvent _processQueueEvt = new AutoResetEvent();
    private ConcurrentQueue<MailMessage> _queue = new ConcurrentQueue<MailMessage>();
    private Thread _dispatcherThread;
    private volatile bool _enabled = true;

    #region Implementation of IRegisteredObject

    public void Stop(bool immediate)
    {
        if (_dispatcherThread != null && _dispatcherThread.IsAlive)
        {
            // is not a immediate stop, we can wait for the queue to empty
            if (!immediate)
            {
                // stop to accept new requests...
                _enabled = false;
                // awake dispatcher thread, so it can quit if the queue is empty
                _processQueueEvt.Set();
                // and wait for a while but not forever.
                _dispatcherThread.Join(TimeSpan.FromSeconds(30));
            }
            else
            {
                // host env will recycle now, nothing to do
                _dispatcherThread.Abort();
            }
        }
        // remove the service from host
        HostingEnvironment.UnregisterObject(this);
    }

    #endregion

    public void Start()
    {
        _dispatcherThread = new Thread(ProcessQueue);
        _dispatcherThread.Start();
    }

    private void ProcessQueue()
    {
        while (_enabled)
        {
            _processQueueEvt.WaitOne();
            MailMessage message;
            while (_queue.TryDequeue(out message)) { /* send mail ...*/}
        }
    }

    public void DispatchEmail(MailMessage message)
    {
        if (!_enabled) throw new Exception("....");
        _queue.Enqueue(message);
        _processQueueEvt.Set();
    }
}
Run Code Online (Sandbox Code Playgroud)

启动服务并在主机上注册.

var mailService = new MailDispatchService();
System.Web.Hosting.HostingEnvironment.RegisterObject(mailService);
mailService.Start();

var message = new MailMessage();
mailService.DispatchEmail(message);    
Run Code Online (Sandbox Code Playgroud)

  • 根据[HttpRuntime.UsingIntegratedPipeline](http://msdn.microsoft.com/en-us/library/system.web.httpruntime.usingintegratedpipeline.aspx),我正在使用集成管道.这是个好消息! (2认同)