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