代码之家  ›  专栏  ›  技术社区  ›  Michael Buen

这个thread.abort()正常且安全吗?

  •  20
  • Michael Buen  · 技术社区  · 16 年前

    我创建了一个自定义的自动完成控件,当用户按键时,它会在另一个线程上查询数据库服务器(使用远程处理)。当用户输入速度非常快时,程序必须取消先前执行的请求/线程。

    EndInvoke无法终止线程,它仍将完成待终止线程的操作。我仍然会在线程上使用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()的代码,使用计数器:

    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);
          }
       }
    }
    
    5 回复  |  直到 16 年前
        1
  •  31
  •   Community CDub    7 年前

    不,是的 安全 Thread.Abort() 在最好的情况下,它足够粗略,但是在这种情况下,您的控件对委托回调中的操作没有控制权。你不知道应用程序的其他部分将处于什么状态,当再次呼叫代理时,你可能会发现自己处于一个受伤的世界。

    速度慢,或者用户正在键入 那个 快,那么他们可能不会期望自动完成。

    关于更新的(Abort()-免费)代码:

    您现在正在为(可能的)启动一个新线程 每个按键 . 这不仅会降低性能,而且没有必要——如果用户没有暂停,他们可能不会寻找控件来完成他们正在键入的内容。

    我之前提到过这个,但是 P Daddy said it better

    你最好只是实施一下 一个一次性计时器,可能有一个 半秒超时,并重置它 在每次击键时。

    开始等待自动完成启动。玩弄延迟-半秒钟可能适合不耐烦的触摸打字员,但如果您的用户稍微放松一点。。。或者你的数据库有点慢。。。然后,您可以通过2-3秒或更长的延迟获得更好的结果。不过,这项技术最重要的部分是 reset the timer on every keystroke

    除非您期望数据库请求实际上 悬挂 ,不必费心尝试允许多个并发请求。如果请求当前正在进行中,请等待其完成,然后再发出另一个请求。

        2
  •  11
  •   P Daddy    16 年前

    There are many warnings all over the net 关于使用 Thread.Abort . 我会建议避免,除非它真的需要,在这种情况下,我不认为它是。您最好只实现一个一次性计时器,可能有半秒的超时时间,并在每次击键时重置它。这样,您昂贵的操作只会在半秒或更长时间(或您选择的任何长度)的用户不活动后发生。

        3
  •  4
  •   Brig Lamoreaux Brig Lamoreaux    16 年前

    你可能想看看 An Introduction to Programming with C# Threads

    在第4页,他说:

    当你看到 线程命名空间,您将 (或应该)被射程吓倒 互斥;等待或自动回复; 有一个简单的答案:使用 以及中断方法。那些是 论文的其余部分。现在,你 系统。线程,虽然不好 第9节为你概述一下。

        4
  •  3
  •   C. Dragon 76    16 年前

    不,我将避免调用线程。在您自己的代码上中止。您希望自己的后台线程正常完成,并自然展开其堆栈。我可能只考虑调用thord.abt是在一个场景中,我的代码在另一个线程(比如插件场景)上托管外国代码,我真的想中止国外代码。

        5
  •  0
  •   Daniel MoÅ¡mondor    13 年前

    使用 Thread.Abort

    否则,不要这样做。那就更糟了

    try
    {
    //  do stuff
    }
    catch { } //  gulp the exception, don't do anything about it
    

    安全网。。。