[英]Is this thread.abort() normal and safe?
我創建了一個自定義自動完成控件,當用戶按下一個鍵時,它將在另一個線程上查詢數據庫服務器(使用遠程處理)。 當用戶快速鍵入時,程序必須取消先前執行的請求/線程。
我以前首先將其實現為AsyncCallback,但我發現它很麻煩,要遵循的內部規則過多(例如AsyncResult,AsyncState,EndInvoke),另外您還必須檢測BeginInvoke'd對象的線程,以便可以終止先前執行的線程。 此外,如果我繼續執行AsyncCallback,則那些AsyncCallbacks上沒有任何方法可以正確終止先前執行的線程。
EndInvoke無法終止線程,它仍將完成待終止線程的操作。 我仍然會最終在線程上使用Abort()。
因此,我決定僅使用純線程方法來實現它,而無需使用AsyncCallback。 這是thread.abort()正常且對您安全嗎?
public delegate DataSet LookupValuesDelegate(LookupTextEventArgs e);
internal delegate void PassDataSet(DataSet ds);
public class AutoCompleteBox : UserControl
{
Thread _yarn = null;
[System.ComponentModel.Category("Data")]
public LookupValuesDelegate LookupValuesDelegate { set; get; }
void DataSetCallback(DataSet ds)
{
if (this.InvokeRequired)
this.Invoke(new PassDataSet(DataSetCallback), ds);
else
{
// implements the appending of text on textbox here
}
}
private void txt_TextChanged(object sender, EventArgs e)
{
if (_yarn != null) _yarn.Abort();
_yarn = new Thread(
new Mate
{
LookupValuesDelegate = this.LookupValuesDelegate,
LookupTextEventArgs =
new LookupTextEventArgs
{
RowOffset = offset,
Filter = txt.Text
},
PassDataSet = this.DataSetCallback
}.DoWork);
_yarn.Start();
}
}
internal class Mate
{
internal LookupTextEventArgs LookupTextEventArgs = null;
internal LookupValuesDelegate LookupValuesDelegate = null;
internal PassDataSet PassDataSet = null;
object o = new object();
internal void DoWork()
{
lock (o)
{
// the actual code that queries the database
var ds = LookupValuesDelegate(LookupTextEventArgs);
PassDataSet(ds);
}
}
}
當用戶連續鍵入鍵時取消上一個線程的原因,不僅是為了防止文本的添加發生,而且是為了取消上一個網絡往返,因此該程序不會因為連續執行而消耗過多的內存。網絡操作。
我擔心是否完全避免使用thread.Abort(),該程序可能會占用太多內存。
這是不帶thread.Abort()的代碼,使用一個計數器:
internal delegate void PassDataSet(DataSet ds, int keyIndex);
public class AutoCompleteBox : UserControl
{
[System.ComponentModel.Category("Data")]
public LookupValuesDelegate LookupValuesDelegate { set; get; }
static int _currentKeyIndex = 0;
void DataSetCallback(DataSet ds, int keyIndex)
{
if (this.InvokeRequired)
this.Invoke(new PassDataSet(DataSetCallback), ds, keyIndex);
else
{
// ignore the returned DataSet
if (keyIndex < _currentKeyIndex) return;
// implements the appending of text on textbox here...
}
}
private void txt_TextChanged(object sender, EventArgs e)
{
Interlocked.Increment(ref _currentKeyIndex);
var yarn = new Thread(
new Mate
{
KeyIndex = _currentKeyIndex,
LookupValuesDelegate = this.LookupValuesDelegate,
LookupTextEventArgs =
new LookupTextEventArgs
{
RowOffset = offset,
Filter = txt.Text
},
PassDataSet = this.DataSetCallback
}.DoWork);
yarn.Start();
}
}
internal class Mate
{
internal int KeyIndex;
internal LookupTextEventArgs LookupTextEventArgs = null;
internal LookupValuesDelegate LookupValuesDelegate = null;
internal PassDataSet PassDataSet = null;
object o = new object();
internal void DoWork()
{
lock (o)
{
// the actual code that queries the database
var ds = LookupValuesDelegate(LookupTextEventArgs);
PassDataSet(ds, KeyIndex);
}
}
}
不,這是不是安全。 最好的情況下, Thread.Abort()
足夠粗略,但是在這種情況下,您的控件無法(委托)控制委托回調中的操作。 您不知道該應用程序的其余部分將保留在什么狀態,並且當需要再次致電該委托人時,很可能會陷入困境。
設置一個計時器。 文本更改后稍等片刻,然后再調用委托。 然后等待它返回,然后再次調用它。 如果它是緩慢的,或用戶打字那么快,那么他們可能不希望自動完成反正。
現在,您將為(可能) 每個按鍵啟動一個新線程。 這不僅會降低性能,而且沒有必要-如果用戶沒有暫停,他們很可能不在尋找該控件來完成輸入的內容。
我之前提到過,但是P Daddy說的更好 :
您最好只實現一個一鍵式定時器(可能會有一個半秒的超時),並在每次擊鍵時將其重置。
想想看:即使是快速連接到快速數據庫,快速打字員也可能在第一個自動完成回調有機會完成之前創建線程得分。 但是,如果你們推遲,直到最后一次按鍵后的短時間內請求已過,那么你打的是甜蜜點,其中用戶已鍵入了所有他們想要更好的機會(或所有他們知道!),是剛剛開始等待自動完成功能開始。延遲播放-半秒可能適合不耐煩的觸摸打字員,但是如果您的用戶更放松...或者您的數據庫更慢...那么您可能會延遲2-3秒甚至更長的時間來獲得更好的結果。 但是,此技術最重要的部分是您reset the timer on every keystroke
。
並且除非您期望數據庫請求實際掛起 ,否則請不要試圖允許多個並發請求。 如果當前正在處理一個請求,請在另一個請求完成之前等待它完成。
您可能想看一看C#線程編程入門 -Andrew D. Birrell。 他概述了有關C#中的線程的一些最佳實踐。
在第4頁上,他說:
當您查看“ System.Threading”命名空間時,您將(或應該)對您面臨的選擇范圍感到畏縮:“ Monitor”或“ Mutex”; “等待”或“ AutoResetEvent”; “中斷”還是“中止”? 幸運的是,有一個簡單的答案:使用“ lock”語句,“ Monitor”類和“ Interrupt”方法。 這些就是我將在本文其余大部分內容中使用的功能。 現在,您應該忽略“ System.Threading”的其余部分,盡管我將在第9節為您概述它。
不,我避免在自己的代碼上調用Thread.Abort。 您希望自己的后台線程正常完成並自然地釋放其堆棧。 我唯一可能考慮調用Thread.Abort的情況是在我的代碼將外部代碼托管在另一個線程上的情況下(例如插件方案),而我真的想中止外部代碼。
相反,在這種情況下,您可以考慮僅對每個后台請求進行版本控制。 在回調中,請忽略“過期”的響應,因為服務器響應可能以錯誤的順序返回。 我不必擔心中止已經發送到數據庫的請求。 如果您發現數據庫沒有響應或太多請求不堪重負,請考慮使用計時器,如其他建議的那樣。
退出應用程序時,請僅將Thread.Abort
用作最后手段,並知道所有重要資源都已安全釋放。
否則,請勿這樣做。 那就更糟了
try
{
// do stuff
}
catch { } // gulp the exception, don't do anything about it
安全網
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.