首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > .NET > C# >

c#winform多线程假死(不能拖动窗口,窗口未响应)有关问题

2013-09-06 
c#winform多线程假死(不能拖动窗口,窗口未响应)问题,在线等public delegate void treeinvoke(int i)publi

c#winform多线程假死(不能拖动窗口,窗口未响应)问题,在线等

public delegate void treeinvoke(int i);
        public Form1()
        {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            Console.WriteLine("AAA");
            System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(this.startupdate));
            th.IsBackground = true;
            th.Start();
            Console.WriteLine("BBB");
        }

        private void startupdate()
        {
            Console.WriteLine("CCC");
            this.BeginInvoke(new treeinvoke(this.UpdateTreeView), 0);
            Console.WriteLine("DDD");

        }

        private void UpdateTreeView(int j)
        {

            try
            {
                Console.WriteLine("EEE");
                Thread.Sleep(5000);
                Console.WriteLine("FFF");
            }
            catch (Exception ex)
            {

            }

        }


winform中,为什么运行点击button1的时候,会出现假死?不是异步执行的吗?为什么非要等执行完Thread.Sleep(5000);后才可以拖动窗口?


另外,就算我不用Thread.Sleep(5000);如果我这里读取数据库时间太长,也会出现假死,为什么呢,异步。。。。
在线等,谢谢
在线急等,谢谢大家解答 c#winform多线程假死问题
[解决办法]
不清楚LZ想干什么!
用Task,替代 Thread


  public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Console.WriteLine("AAA"); 
            Task.Factory.StartNew(() =>
            {
                this.startupdate();
            });
            Console.WriteLine("BBB");
        }
        private void startupdate()
        {
            Console.WriteLine("CCC");
            Task.Factory.StartNew(() =>
            {
                this.UpdateTreeView(0);
            });
            Console.WriteLine("DDD");
        } 
        private void UpdateTreeView(int j)
        { 
            try
            {
                Console.WriteLine("EEE");


                Thread.Sleep(5000);
                Console.WriteLine("FFF");
            }
            catch (Exception ex)
            {

            } 
        }


[解决办法]
Control.BeginInvoke会通知主线程执行,也就是说UpdateTreeView在主线程执行。
B - E -F是主线程
C-D是子线程

[解决办法]
去掉BeginInvoke,直接this.UpdateTreeView()
[解决办法]
你要做什么程序。。。。
[解决办法]
单纯的例子网上也很多,如果只是作为理解和尝试,参看
http://www.csharpwin.com/csharpspace/11948r7265.shtml
[解决办法]
不要直接在主线程中出现while true之类 然后调用线程去处理的方法。。否则会卡死,你单独起一个进程
------解决方案--------------------


又是界面假死的,参考:http://www.cnblogs.com/zhili/archive/2013/05/10/APM.html
[解决办法]

引用:
Quote: 引用:

理解,线程和异步是两回事。
你要多线程的话,BeginInvoke换成再new个线程好了。

麻烦您能给明示下吗?是UpdateTreeView方法里new新线程吗?

只是针对你这个例子

            System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(UpdateTreeView));
            th.IsBackground = true;
            th.Start();

不明太你所需要的 多线程+异步 处理数据的意义?
[解决办法]
引用:
Quote: 引用:

Quote: 引用:

Quote: 引用:

理解,线程和异步是两回事。
你要多线程的话,BeginInvoke换成再new个线程好了。

麻烦您能给明示下吗?是UpdateTreeView方法里new新线程吗?

只是针对你这个例子

            System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(UpdateTreeView));
            th.IsBackground = true;
            th.Start();

不明太你所需要的 多线程+异步 处理数据的意义?

我要做的项目是:获取某些网站的数据,直接把整站所有链接都下载下来,所以用多线程异步同时下载多个网站。。。。用多线程+异步没问题吧?

线程方式:点个按钮启动后台线程,开始下载链接。前台UI该干嘛干嘛。适合耗时很长的情况
异步:点下载按钮,开始下载数据,前台UI loading等待下载结束。适合速度较快的情况

别再多线程+异步了。






[解决办法]
你要知道两件事:
1. UI的变化都是瞬时的,不需要,也不允许在非UI线程里update UI。
2. 非UI操作有可能是耗时的,如果将其放到UI线程里则“卡UI",所以你要放到后台线程里。与此同时,如果在耗时操作中,你需要不断更新UI,则需要在UI线程上使用Invoke或BeginInvoke。
[解决办法]
问题已经帮你解决了,因为你在代码中调用了this.BeginInvoke方法,这个方法的意思是让UI线程去执行你传递的方法的,然而你传递的方法却有thread.sleep(5000)这样就导致UI线程阻塞了,这样肯定界面就会假死了。具体修改为:

private void button1_Click(object sender, EventArgs e)
        {
            Console.WriteLine("AAA");

            ThreadPool.QueueUserWorkItem(startupdate, null);


            Console.WriteLine("BBB");

        }
        private void startupdate(object c)
        {
            // 这里也可以执行耗时的数据库操作,耗时的操作放在后台线程执行
            Thread.Sleep(5000);
            

        }


[解决办法]
引用:
Quote: 引用:

你要知道两件事:
1. UI的变化都是瞬时的,不需要,也不允许在非UI线程里update UI。
2. 非UI操作有可能是耗时的,如果将其放到UI线程里则“卡UI",所以你要放到后台线程里。与此同时,如果在耗时操作中,你需要不断更新UI,则需要在UI线程上使用Invoke或BeginInvoke。


WinForm的还是WPF的?
[解决办法]
卤煮,很牛逼。 在下佩服。哈哈。
[解决办法]

把 private void UpdateTreeView(int j) 中的 这句   Thread.Sleep(5000);
移动到startupdate()  BeginInvoke前面就好了. 
       
[解决办法]
你这样的代码肯定会出现异常的,因为你在后台线程访问了UI线程创建的控件了,你有没有仔细看我的文章介绍的了,反正更新界面的操作一定是由UI线程去完成的,然而看你代码的需求是UI线程需要去执行加载10000个节点,这样肯定会造成界面假死的,不管怎样有多多线程,都不可能实现的, 你只能在UI线程显示进度的了,你可以在访问在获得所有节点然后全部加载到TreeView中了
[解决办法]
这个问题我说过很多遍了,不能整体回调,必须拆分处理,你这里耗时操作主要在UpdateTreeView方法里面,你对这个方法整体回调,等于没用多线程。
正确的做法是,在UpdateTreeView方法内部,单独将UI操作进行BeginInvoke
        private void UpdateTreeView(int j)
        {
            try
            {
                System.Windows.Forms.TreeNode tn;
                for (int i = 0; i <= 10; i++)
                {
                    string strPageData = GetHttp("http://news.163.com/rank/", Encoding.GetEncoding("GB2312"));


                    //下面是UI的操作,单独封装。
                    Action updateUI = new Action(() =>
                    {
                        tn = new System.Windows.Forms.TreeNode(j.ToString() + "-" + i.ToString());
                        treeView1.BeginUpdate();
                        treeView1.Nodes[0].Nodes.Insert(0, tn);
                        treeView1.Nodes[0].Expand();
                        treeView1.EndUpdate();
                    });
                    treeView1.BeginInvoke(updateUI);
                }
            }
            catch (Exception ex)
            {

            }
        }


[解决办法]
楼主这是没有搞明白Control.BeginInvoke 与 Delegate.BeginInvoke
在C#中Control有且只有一个主线程 所有涉及到Control的调用都是在这个主线程上操作的,这就是为什么你第一次的代码中界面会假死,应为你用的this.BeginInvoke 看似是用了异步,其实是在Control的主线程上操作的 要解决这个问题就是用Delegate.BeginInvoke替换Control.BeginInvoke 在你的代码中可以如下是实现

 treeinvoke inv = new treeinvoke(UpdateTreeView);
 inv.BeginInvoke(i, null, null);

再说你的界面不能显示问题,还是上面说到的Control.BeginInvoke 与 Delegate.BeginInvoke的区别,你解决了界面的假死是因为使用了Delegate.BeginInvoke另外启动了一个线程来操作数据,但是显示的时候没有回到Control的主线程上来,这里的解决办法就是用this.BeginInvoke让界面的操作回到主线程上来操作 

        private delegate void AddNode(int j, int i);



        private void UpdateTreeView2(int j, int i)
        {
            TreeNode tn = new TreeNode(j.ToString() + "-" + i.ToString());
            this.treeView1.Nodes[0].Nodes.Insert(0, tn);
            this.treeView1.Nodes[0].Expand();
            this.treeView1.Refresh();
        }

        private void UpdateTreeView(int j)
        {
            //反正这里执行耗时间操作Start
            try
            {
                 
                TreeNode tn;
                for (int i = 0; i <= 10; i++)
                {
                    string strPageData = GetHttp("http://news.163.com/rank/", Encoding.GetEncoding("GB2312"));
                    this.BeginInvoke(new AddNode(UpdateTreeView2), j, i);
                }
                
            }
            catch (Exception ex)
            {
 
            }
            //反正这里执行耗时间操作End
        }




不知道你看明白了没有
[解决办法]
引用:

Quote: 引用:



把 private void UpdateTreeView(int j) 中的 这句   Thread.Sleep(5000);
移动到startupdate()  BeginInvoke前面就好了. 
       

麻烦看看我34楼的代码。。。。可以不要Thread.sleep(),但是运行的时候,不能拖动UI界面


楼主其实根本没理解我的意思,把Thread.Sleep()前移意思是把耗时的操作移动到线程里面执行,
界面更新只执行短操作.

比如你下面的例子  GetHttp 这个耗时的操作,就可以前移呀.这样就不会卡界面了.

热点排行