Я создал настраиваемый элемент автозаполнения, когда пользователь нажимает клавишу, он запрашивает сервер базы данных (используя Remoting) в другом потоке. Когда пользователь набирает очень быстро, программа должна отменить ранее выполняемый запрос/поток.
Я ранее реализовал его как 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() вообще, программа может потреблять слишком много памяти.
здесь код без потока .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);
}
}
}