【发布时间】:2009-01-07 18:16:53
【问题描述】:
我创建了一个自定义自动完成控件,当用户按下一个键时,它会在另一个线程上查询数据库服务器(使用远程处理)。当用户打字非常快时,程序必须取消之前执行的请求/线程。
我之前先将它实现为 AsyncCallback,但我觉得它很麻烦,要遵循的内部规则太多(例如 AsyncResult、AsyncState、EndInvoke),而且您必须检测 BeginInvoke 对象的线程,因此您可以终止之前执行的线程。此外,如果我继续 AsyncCallback,那些 AsyncCallback 上没有任何方法可以正确终止先前执行的线程。
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);
}
}
}
【问题讨论】:
-
刚刚看到您的编辑...您实际上可能想提交另一个关于定时自动完成的问题;这本身就是一个有点棘手的主题。
标签: c# multithreading