• C#里的Thread.Join与Control.Invoke死锁情况


    Thread.Join会导致调用线程挂起, 等待Thread结束后继续执行.

    此时若调用线程为主线程(UI线程)同时Thread里面调用了控件的Invoke方法, 则有可能会导致死锁

    代码如下:

     1     public delegate void InvokeHandler();
     2     static class Extensions
     3     {
     4         public static void SafeInvoke(this Control control, InvokeHandler handler)
     5         {
     6             if (control.InvokeRequired)
     7             {
     8                 control.Invoke(handler);
     9             }
    10             else
    11             {
    12                 handler();
    13             }
    14         }
    15         public static void BeginSafeInvoke(this Control control, InvokeHandler handler)
    16         {
    17             if (control.InvokeRequired)
    18             {
    19                 control.BeginInvoke(handler);
    20             }
    21             else
    22             {
    23                 handler();
    24             }
    25         }
    26     }
     1     class UserInfo
     2     {
     3         public string Account;
     4         public string Password;
     5         public ListViewItem LVItem;
     6         public void SetListViewItemText(int subindex, string text)
     7         {
     8             LVItem.ListView.BeginSafeInvoke(() => //起初使用的是SafeInvoke, 会导致死锁
     9             {
    10                 LVItem.SubItems[subindex].Text = text;
    11                 LVItem.ListView.Refresh();
    12             });
    13             
    14         }
    15     }
     1         public void Stop()
     2         {
     3             _event.Set(); //通知线程要退出
     4             _thread.Join(); //A. 等待线程处理完(此处和B导致死锁)
     5             _thread = null;
     6             _event.Close();
     7             _event = null;
     8         }
     9         private void Run()
    10         {
    11             _user.SetListViewItemText(1, "正在登录...");
    12             _loginTime = DateTime.Now;
    13             while (_event.WaitOne(5000) == false)
    14             {
    15                 TimeSpan span = DateTime.Now - _loginTime;
    16 
    17                 _user.SetListViewItemText(1, string.Format("已登录: {0}", span.ToString()));
    18             }
    19             _user.SetListViewItemText(1, "已停止"); //B. Invoke方式来更新控件内容(此处和A导致死锁)
    20         }

     通过代码可以看出, 起初更新相关控件内容时采用的Invoke方式, Invoke会使调用线程挂起并在UI线程上执行相关方法, 直到执行完毕后, 调用线程才会继续.

    也就是说Invoke会等待UI操作完成才继续. 而上面代码里由于在UI线程上调用了Stop, 在A处UI线程会处于挂起状态, 这时Run方法所处的线程要更新UI, 调用了Invoke, 这时因为UI线程处于挂起状态, Invoke得不到执行, 所以Run方法所在的线程也挂起了, 永远不会结束. 这样A处就一直挂在那了, 两个都挂着, 就行成死锁了.

    解决方案也很简单: 就是把Invoke换成BeginInvoke, BeginInvoke是异步方式执行, 也就是说调用线程执行BeginInvoke不会挂起, 这样就不会导致死锁了.

  • 相关阅读:
    SpringMVC请求静态资源
    Spring视图和视图解析器
    @ModelAttribute运行流程
    SpringMVC模型数据处理
    SpringMVC简单映射请求参数介绍
    队列和栈的问题
    非比较排序——计数排序、基数排序、桶排序
    递归
    对数器的使用
    常见的比较排序
  • 原文地址:https://www.cnblogs.com/Athrob/p/4137637.html
Copyright © 2020-2023  润新知