2010-05-31 121 views
1

我有一個Web服務方法FetchNumber(),其獲取來自數據庫的數,然後返回給調用者。但在它將號碼返回給調用者之前,它需要將此號碼發送到另一個服務,以便實例化並運行BackgroundWorker,其工作是將此號碼發送到另一個服務。Web服務進行異步調用

public class FetchingNumberService : System.Web.Services.WebService 
{ 
    [WebMethod] 
    public int FetchNumber() 
    { 
     int value = Database.GetNumber(); 
     AsyncUpdateNumber async = new AsyncUpdateNumber(value); 
     return value; 
    } 
} 

public class AsyncUpdateNumber 
{ 
    public AsyncUpdateNumber(int number) 
    { 
     sendingNumber = number; 

     worker = new BackgroundWorker(); 
     worker.DoWork += asynchronousCall; 
     worker.RunWorkerAsync(); 
    } 

    private void asynchronousCall(object sender, DoWorkEventArgs e) 
    { 
     // Sending a number to a service (which is Synchronous) here 
    } 

    private int sendingNumber; 
    private BackgroundWorker worker; 

} 

我不想阻止Web服務(FetchNumber())在發送這個號碼到另一個服務,因爲它需要很長的時間,主叫方不會關心發送的數量到另一個服務。來電者希望這個儘快返回。

FetchNumber()使得後臺工作,並運行它,然後完成(而工人在後臺線程中運行)。我不需要任何進度報告或後臺工作人員的返回值。這更多是一個難忘的概念。

我的問題是這樣的。由於Web服務對象是每個方法調用實例化的,當被調用的方法(在這種情況下爲FetchNumber())完成時會發生什麼,而它安裝並運行的後臺工作器仍在運行?

會發生什麼事到後臺線程? GC何時收集服務對象?這是否會阻止後臺線程正確執行到最後?後臺線程有沒有其他副作用?

感謝任何輸入。

回答

0

從我從你的代碼中看到我想說的是,他們相互引用,沒有代碼,打破循環引用既沒有後臺工作,也不是AsyncUpdateNumber類的實例被收集。

循環引用由引用BackgroundWorker的AsyncUpdateNumber類創建,並使用BackgroundWorker創建對AsyncUpdateNumber類的實例的引用來註冊事件。

所以,你能做些什麼?考慮使用下列選項而不是BackgroundWorker的一個:

  1. 使用線程。
  2. 使用BeginInvoke。
  3. 使用ThreadPool。

樣品1:

var thread = new Thread(new ParameterizedThreadStart((v) => { /* do stuff */ })); 
thread.Start(value); 

樣品2:

var func = new Action<int>(v => { /* do stuff */ }); 
func.BeginInvoke(value, null, null); 

樣品3:

var threadProc = new Action<object>(v => { /* do stuff - note v is of tyoe object */ }); 
ThreadPool.QueueUserWorkItem(new WaitCallback(threadProc)); 

編輯:

要回答從原來的職位你的問題:執行線程的方法始終運行,直到它完成,該方法是否無關宣佈退出與否。即使上面的匿名方法也會運行直到完成。這裏應用的概念稱爲閉包(AFAIK),在匿名方法的情況下,即使它們沒有在方法本身中聲明,它甚至可以保持所有引用的變量存活。

但是,您的課程是循環引用的主要示例,只有在應用程序過程完成後纔會回收。這是.NET中這些更微妙的事情之一 - 即使在管理系統中 - 可能導致內存泄漏... 事件創建強引用

儘管在單次調用時這不會引起任何問題,但是一旦您再次發出一些呼叫,它確實可能會成爲問題。

+0

如果BackgroundWorker只是AsyncUpdateNumber的構造函數的方法範圍的局部變量,而不是其字段?對我來說,重要的問題不僅是這些對象是否會被收集,而且即使起始線程沒有引用後臺工作線程,線程是否會完成其工作? – 2010-05-31 20:39:50

+0

當您在AsyncUpdateNumber類的構造函數中啓動worker時,該方法將完成。但是,由於所有調用都會創建永遠不會收集的新對象,因此會創建內存泄漏。如果*有*使用BackgroundWorker,只要Worker已經啓動,您應該立即從BackgroundWorker註銷事件實例。方法完成後,將BackgroundWorker設置爲null。不過,我認爲這是漫長的...看樣品和簡單。 – AxelEckenberger 2010-05-31 20:54:14

+0

我不能這樣做,因爲我需要把作業的異步部分分成一個類(用於可重用性問題)。我可能會把你的樣本放入一個班級,但這也讓我面對原始問題。由於第一個線程只創建一個Async類的實例作爲局部變量,所以當它離開作用域時(即方法結束),它應該被刪除。當其他線程完成其工作時(無論是BW,Thread,BeginInvoke還是Pool),會發生什麼情況。 GC是否爲每個線程都有單獨的參考樹? – 2010-06-01 06:37:37

0

重寫單捲筒紙方法

[WebMethod] 
public int FetchNumber() 
{ 
    int value = Database.GetNumber(); 
    AsyncUpdateNumber async = new AsyncUpdateNumber(value); 
    return value; 
} 

爲兩個與和異步委託:

public delegate AsyncUpdateNumber GetAsyncUpdateNumber(object state, int value); 

[WebMethod] 
public IAsyncResult BeginFetchNumber(AsyncCallback cb, object state) 
{ 
    int value = Database.GetNumber(); 
    AsyncUpdateNumber async = new AsyncUpdateNumber(value); 
    GetAsyncUpdateNumber getAsyncUpdateNumber = new GetAsyncUpdateNumber(async.DoLongRunningThing); 

    return getAsyncUpdateNumber.BeginInvoke(state, cb, getAsyncUpdateNumber); 
} 

[WebMethod] 
public int EndFetchNumber(IAsyncResult res) 
{ 
    GetAsyncUpdateNumber getAsyncUpdateNumber = (GetAsyncUpdateNumber)res.AsyncState; 

    return getAsyncUpdateNumber.EndInvoke(res); 
} 

將顯示爲稱爲FetchNumber()單個web方法。 BeginFetchNumber()中的BeginInvoke()將異步執行。我不得不爲AsyncUpdater編寫一個名爲DoLongRunningThing的方法供委託執行。將您需要從構造函數中完成異步的任務移至此新方法中。希望這可以幫助。