首先,不推薦在ASP.NET后臺(tái)中,啟動(dòng)Long-Running的任務(wù)。因?yàn)闊o(wú)論是用的Task還是ThreadPool.QueueUserWorkItem,ASP.NET不會(huì)知道它們?cè)诤笈_(tái)運(yùn)行,這會(huì)產(chǎn)生一些問題,比如:
站在用戶的角度思考問題,與客戶深入溝通,找到蕭縣網(wǎng)站設(shè)計(jì)與蕭縣網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都做網(wǎng)站、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、國(guó)際域名空間、虛擬主機(jī)、企業(yè)郵箱。業(yè)務(wù)覆蓋蕭縣地區(qū)。當(dāng)修改web.config的時(shí)候,會(huì)觸發(fā)Appdomain被回收(盡管此時(shí)IIS web服務(wù)器進(jìn)程w3wp.exe仍然活著),IIS本身也會(huì)每29小時(shí)回收應(yīng)用程序池,這都會(huì)導(dǎo)致后臺(tái)線程被終止,從而引發(fā)異常。
當(dāng)ASP.NET要回收AppDomain,它會(huì)讓已經(jīng)存在的請(qǐng)求處理完再回收,ASP.NET和IIS服務(wù)器也知道這些請(qǐng)求正在運(yùn)行,所以等它們完成。問題是ASP.NET不知道任何后臺(tái)線程比如一個(gè)計(jì)時(shí)器或者其他,它只知道和request相關(guān)的操作。
事實(shí)上,在后臺(tái)長(zhǎng)時(shí)間的運(yùn)行某些任務(wù)實(shí)在不是web server該做的事情,通常都可以用其他的方式來(lái)避免這樣做,比如:
用console application和windows任務(wù)管理器,或者使用Windows服務(wù)等。
但是,如果確定要這樣做,那么在ASP.NET中也有些辦法保證后臺(tái)任務(wù)能夠安全的退出。
方法一,使用IRegisteredObject接口。
通過(guò)IRegisteredObject接口,并且調(diào)用HostingEnvironment.RegisterObject方法在ASP.NET中注冊(cè)它。
當(dāng)Appdomain要被回收的時(shí)候,會(huì)調(diào)用已注冊(cè)對(duì)象中的IRegisteredObject中的Stop方法。
public interface IRegisteredObject { void Stop(bool immediate); }
已注冊(cè)對(duì)象沒有被取消注冊(cè)(即沒有調(diào)用HostingEnvironment.UnregisterObject方法來(lái)取消注冊(cè)),那么Stop方法會(huì)被調(diào)用兩次,第一次調(diào)用的時(shí)候,immediate參數(shù)為false,此時(shí)如果已注冊(cè)對(duì)象已經(jīng)停止了,那么應(yīng)該調(diào)用 HostingEnvironment.UnregisterObject方法來(lái)取消注冊(cè)。如果還不取消注冊(cè),那么30秒后,該方法會(huì)被再次執(zhí)行(這是最后的機(jī)會(huì)),不同的是此時(shí)傳入的immediate參數(shù)為true,此時(shí)注冊(cè)對(duì)象必須先調(diào)用 UnregisterObject 方法然后返回;否則應(yīng)用程序管理器將移除該對(duì)象的注冊(cè)。
使用IRegisteredObject接口并不是用來(lái)處理后臺(tái)Long-Running任務(wù)的,但是這個(gè)功能可以讓你安全的退出后臺(tái)任務(wù)。
舉個(gè)例子,如下:
public class JobHost : IRegisteredObject { private readonly object _lock = new object(); private bool _shuttingDown; public JobHost() { HostingEnvironment.RegisterObject(this); } public void Stop(bool immediate) { lock (_lock) { _shuttingDown = true; } HostingEnvironment.UnregisterObject(this); } public void DoWork(Action work) { lock (_lock) { if (_shuttingDown) { return; } work(); } } }
如上面的代碼,ASP.NET要回收Appdomain時(shí),Stop 方法會(huì)被調(diào)用,這個(gè)方法會(huì)獲得一個(gè)鎖,在DoWork方法也獲得同樣的所,這樣的話,DoWork在運(yùn)行的時(shí)候,Stop方法不得不等待。
方法二,使用HostingEnvironment.QueueBackgroundWorkItem
HostingEnvironment.QueueBackgroundWorkItem 方法在.NET4.5.2中引入,QueueBackgroundWorkItem (QBWI) 通過(guò)ASP.NET運(yùn)行時(shí),注冊(cè)后臺(tái)任務(wù)。這樣的話,由于ASP.NET知道有后臺(tái)任務(wù),他就不會(huì)立即回收Appdomain,當(dāng)然,這不意味著后臺(tái)任務(wù)可以做任何事情。當(dāng)ASP.NET需要做回收的時(shí)候,它可以通過(guò)一個(gè)CancellationToken通知后臺(tái)任務(wù),并且等待30s讓工作完成。如果30秒內(nèi)沒問出,那么這個(gè)工作也會(huì)消失了。關(guān)于QueueBackgroundWorkItem的更多細(xì)節(jié),可以通過(guò)
http://blogs.msdn.com/b/webdev/archive/2014/06/04/queuebackgroundworkitem-to-reliably-schedule-and-run-long-background-process-in-asp-net.aspx學(xué)習(xí)。
方法二,使用HangFire
HangFire是一個(gè)開源的類庫(kù),提供簡(jiǎn)單的方法在ASP.NET中執(zhí)行后臺(tái)Long-Running任務(wù)。這個(gè)類庫(kù)需要一些額外的存儲(chǔ)上的支持,SQLServer,Redis或者M(jìn)SMQ。HangFire的資料在http://hangfire.io/
參考資料
http://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx/
http://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html
http://blogs.msdn.com/b/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國(guó)云服務(wù)器,動(dòng)態(tài)BGP最優(yōu)骨干路由自動(dòng)選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動(dòng)現(xiàn)已開啟,新人活動(dòng)云服務(wù)器買多久送多久。