2012-10-03 28 views
0

我必須從提供的DLL調用例程。該DLL需要2到10分鐘才能運行,具體取決於正在運行的PC的速度。一個後臺進程的兩個線程?

我已將DLL調用放入BackgroundWorker線程,以便界面保持響應。

private object Load(string feature) { 
    object result = null; 
    using (var w = new BackgroundWorker()) { 
    w.WorkerReportsProgress = true; 
    w.WorkerSupportsCancellation = true; 
    w.DoWork += delegate(object sender, DoWorkEventArgs e) { 
     e.Result = DAL.get(feature); 
    }; 
    w.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) { 
     progressBar1.Visible = false; 
     if (e.Error != null) { 
     MessageBox.Show(this, e.Error.Message, "Load Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 
     } else { 
     result = e.Result; 
     } 
    }; 
    w.RunWorkerAsync(); 
    if (w.IsBusy) { 
     progressBar1.Style = ProgressBarStyle.Marquee; 
     progressBar1.Visible = true; 
    } 
    } 
    return result; 
} 

這工作,但我不能打電話與正在等待其結果的其他調用此方法,因爲內嵌它會立即返回空值。

所以,我被困在一個ManualResetEvent的實例,試圖讓該方法等待,直到它實際上是在返回前有一個值:

private object Load(string feature) { 
    object result = null; 
    using (var w = new BackgroundWorker()) { 
    var mre = new ManualResetEvent(false); 
    w.WorkerReportsProgress = true; 
    w.WorkerSupportsCancellation = true; 
    w.DoWork += delegate(object sender, DoWorkEventArgs e) { 
     e.Result = DAL.get(feature); 
    }; 
    w.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) { 
     progressBar1.Visible = false; 
     if (e.Error != null) { 
     MessageBox.Show(this, e.Error.Message, "Model Load Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 
     } else { 
     result = e.Result; 
     } 
     mre.Set(); 
    }; 
    w.RunWorkerAsync(); 
    if (w.IsBusy) { 
     progressBar1.Style = ProgressBarStyle.Marquee; 
     progressBar1.Visible = true; 
     progressBar1.Value = 0; 
     const string statusRun = @"\|/-"; 
     const string statusMsg = "Loading Data..."; 
     int loops = 0; 
     do { 
     int index = loops++ % 4; 
     tsStatus.Text = statusMsg + statusRun[index].ToString(); // '\', '|', '/', '-' 
     } while (!mre.WaitOne(200)); 
    } 
    } 
    return result; 
} 

但是,看來,這樣做會導致我所有的CPU時間花在ManualResetEvent的WaitOne方法上,並且Set()觸發器永遠不會被調用。

有沒有人遇到過這種行爲,並找到一個很好的解決方法呢?

我在過去爲它創建瞭解決方法,但它涉及創建第二個線程來運行WaitOne方法,以便第一個線程可以處理DoWork代碼。

回答

2

這工作,但我不能打電話與正在等待其結果的其他調用此方法,因爲內嵌它會立即返回空值。

這就是發明asyncawait關鍵字的原因。沒有簡單的解決方案,這不會阻止線程。

由於您使用的顯然是不提供方法DLL使用任何異步編程模式的實現(如BeginOperation/EndOperation,或者Task -Based異步編程),你不得不擁有一個單獨的工作線程。

你可以做的是:

啓動BackgroundWorkerThread像往常一樣,然後立即返回。不要繼續執行需要冗長的DLL進程的返回值的操作。一旦BackgroundWorkerThread完成後,讓它報告進度,並在ProgressChanged事件處理程序中,您可以檢索冗長DLL進程的結果並繼續操作。或者,您可以使用RunWorkerCompleted事件(實際上可能是更好的選擇)。

與此同時,您可能必須禁用所有可能再次啓動漫長的DLL進程的控件,否則在進程運行時將會發出無效操作。正如Henk Holterman所寫,不要像這樣處理BackgroundWorker

+0

*使用* BackgroundWorker存在問題嗎?我曾多次在多個項目中完成此任務,並且從未造成任何問題。 – jp2code

+0

您在代碼中使用的方式沒問題,因爲在離開'使用'範圍時,工作人員已經完成了。這隻有在後臺工作者*在同一範圍內啓動並完成時才起作用(這基本上使得它的工作同步,但無論如何)。但是,如果您按照我的建議將代碼分解爲兩部分,則無法使用。工作完成後,您必須手動調用Dispose。 – dialer

+1

如果你確實使用'async',你可以簡化大部分代碼到'var result = await Task.Run((=)=> DAL.get(feature));' –

相關問題