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

dbmonths 2013-09-02 04:39:52
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);如果我这里读取数据库时间太长,也会出现假死,为什么呢,异步。。。。
在线等,谢谢
在线急等,谢谢大家解答
...全文
5488 47 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
47 条回复
切换为时间正序
请发表友善的回复…
发表回复
qldsrx 2013-09-06
  • 打赏
  • 举报
回复
引用 42 楼 dbmonths 的回复:
帅哥,不知道你做实验了没有,这样也会卡死。。。
不可能,除非你还是对UpdateTreeView调用BeginInvoke,那个函数必须在异步执行,而不能回调,只有涉及到UI操作的一小部分才能回调,看来你的思维还是不清晰,写到这个程度了还是不清楚问题的所在。
dbmonths 2013-09-04
  • 打赏
  • 举报
回复
在此感谢各位的热心解答,我采用了30楼的这哥们的方法,个人感觉目前问题解决了,我把代码贴上来,遇到同样问题的朋友可以参考下哦(UI界面就一个label一个button,点击button让label的值改变) 好了,结贴,非常感谢大家
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Threading;
using System.Text.RegularExpressions;
using System.Net;

namespace GetWebData
{
    public partial class Form1 : Form    
    {
        public delegate void treeinvoke(int i);
        int num1 = 0;
        int num2 = 10000;
        public Form1()
        {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            Console.WriteLine("AAA");
            

            for (int i = 0; i < 5000; i++)
            {
                ThreadPool.QueueUserWorkItem(startupdate, i);
            }
            
            Console.WriteLine("BBB");
        }
        private delegate void updateui();
        private void TimeEvent(object source, System.Timers.ElapsedEventArgs e)
        {
            this.BeginInvoke(new updateui(upui));
        }
        private void upui()
        {
            label1.Text = num1.ToString() + "/" + num2.ToString();
        }
        private void startupdate(object c)
        {
            System.DateTime dt = DateTime.Now;
 
            Console.WriteLine("CCC");
            UpdateTreeView(int.Parse(c.ToString()));
            Console.WriteLine("DDD");
        }

        private void UpdateTreeView(int j)
        {
            //反正这里执行耗时间操作Start
            try
            {
                string strPageData = GetHttp("http://news.163.com/rank/", Encoding.GetEncoding("GB2312"));
                this.BeginInvoke(new updateui(upui));//这里更新UI
                Console.WriteLine(j.ToString());
                num1 = j;
            }
            catch (Exception ex)
            {

            }
            //反正这里执行耗时间操作End
        }
        public static string GetHttp(string url, Encoding encode)
        {
            string strResult;
            try
            {
                WebClient client = new WebClient();
                client.Encoding = encode;
                strResult = client.DownloadString(url);
            }
            catch
            {
                strResult = "";
            }
            return strResult;
        }

    }
}
游戏人间 2013-09-04
  • 打赏
  • 举报
回复
引用 37 楼 dbmonths 的回复:
[quote=引用 36 楼 wonderfuly 的回复:] 把 private void UpdateTreeView(int j) 中的 这句 Thread.Sleep(5000); 移动到startupdate() BeginInvoke前面就好了.
麻烦看看我34楼的代码。。。。可以不要Thread.sleep(),但是运行的时候,不能拖动UI界面[/quote] 楼主其实根本没理解我的意思,把Thread.Sleep()前移意思是把耗时的操作移动到线程里面执行, 界面更新只执行短操作. 比如你下面的例子 GetHttp 这个耗时的操作,就可以前移呀.这样就不会卡界面了.
dbmonths 2013-09-04
  • 打赏
  • 举报
回复
引用 40 楼 qldsrx 的回复:
这个问题我说过很多遍了,不能整体回调,必须拆分处理,你这里耗时操作主要在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)
            {

            }
        }
帅哥,你跟41楼的应该一样,还是会卡死。。。。
dbmonths 2013-09-04
  • 打赏
  • 举报
回复
引用 41 楼 urhp02857 的回复:
楼主这是没有搞明白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
        }


不知道你看明白了没有
帅哥,不知道你做实验了没有,这样也会卡死。。。
lr5420511 2013-09-04
  • 打赏
  • 举报
回复
耗时运算用子线程,运算出来的结果,用this.BeginInvoke委托主线程更新界面不就好了吗?
ktei2008 2013-09-03
  • 打赏
  • 举报
回复
你要知道两件事: 1. UI的变化都是瞬时的,不需要,也不允许在非UI线程里update UI。 2. 非UI操作有可能是耗时的,如果将其放到UI线程里则“卡UI",所以你要放到后台线程里。与此同时,如果在耗时操作中,你需要不断更新UI,则需要在UI线程上使用Invoke或BeginInvoke。
xiaogui340 2013-09-03
  • 打赏
  • 举报
回复
引用 26 楼 dbmonths 的回复:
[quote=引用 25 楼 xiaogui340 的回复:] [quote=引用 16 楼 dbmonths 的回复:] [quote=引用 10 楼 xiaogui340 的回复:] 理解,线程和异步是两回事。 你要多线程的话,BeginInvoke换成再new个线程好了。
麻烦您能给明示下吗?是UpdateTreeView方法里new新线程吗?[/quote] 只是针对你这个例子

            System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(UpdateTreeView));
            th.IsBackground = true;
            th.Start();
不明太你所需要的 多线程+异步 处理数据的意义?[/quote] 我要做的项目是:获取某些网站的数据,直接把整站所有链接都下载下来,所以用多线程异步同时下载多个网站。。。。用多线程+异步没问题吧?[/quote] 线程方式:点个按钮启动后台线程,开始下载链接。前台UI该干嘛干嘛。适合耗时很长的情况 异步:点下载按钮,开始下载数据,前台UI loading等待下载结束。适合速度较快的情况 别再多线程+异步了。
dbmonths 2013-09-03
  • 打赏
  • 举报
回复
引用 25 楼 xiaogui340 的回复:
[quote=引用 16 楼 dbmonths 的回复:] [quote=引用 10 楼 xiaogui340 的回复:] 理解,线程和异步是两回事。 你要多线程的话,BeginInvoke换成再new个线程好了。
麻烦您能给明示下吗?是UpdateTreeView方法里new新线程吗?[/quote] 只是针对你这个例子

            System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(UpdateTreeView));
            th.IsBackground = true;
            th.Start();
不明太你所需要的 多线程+异步 处理数据的意义?[/quote] 我要做的项目是:获取某些网站的数据,直接把整站所有链接都下载下来,所以用多线程异步同时下载多个网站。。。。用多线程+异步没问题吧?
xiaogui340 2013-09-03
  • 打赏
  • 举报
回复
引用 16 楼 dbmonths 的回复:
[quote=引用 10 楼 xiaogui340 的回复:] 理解,线程和异步是两回事。 你要多线程的话,BeginInvoke换成再new个线程好了。
麻烦您能给明示下吗?是UpdateTreeView方法里new新线程吗?[/quote] 只是针对你这个例子

            System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(UpdateTreeView));
            th.IsBackground = true;
            th.Start();
不明太你所需要的 多线程+异步 处理数据的意义?
dbmonths 2013-09-03
  • 打赏
  • 举报
回复
引用 22 楼 silence1214 的回复:
不要直接在主线程中出现while true之类 然后调用线程去处理的方法。。否则会卡死,你单独起一个进程
麻烦能给个例子吗。。。多线程+异步。。
人生导师 2013-09-03
  • 打赏
  • 举报
回复
silence1214 2013-09-03
  • 打赏
  • 举报
回复
不要直接在主线程中出现while true之类 然后调用线程去处理的方法。。否则会卡死,你单独起一个进程
dbmonths 2013-09-03
  • 打赏
  • 举报
回复
我是要实现多线程+异步处理数据的功能,现在项目需要这样的功能。。。。不是单纯为了学习,是要工作的,希望各位老大给指点下,如何才能实现多线程+异步处理数据?在线等
Code従業員 2013-09-03
  • 打赏
  • 举报
回复
单纯的例子网上也很多,如果只是作为理解和尝试,参看
http://www.csharpwin.com/csharpspace/11948r7265.shtml
dbmonths 2013-09-03
  • 打赏
  • 举报
回复
引用 17 楼 jiaoshiyao 的回复:
引用 15 楼 dbmonths 的回复:
Control.Check....这个不安全,还是要用BeginINvoke的,谢谢
你要做什么程序。。。。
其实什么程序无所谓的,只要实现多线程+异步处理即可,谢谢哦
dbmonths 2013-09-03
  • 打赏
  • 举报
回复
引用 13 楼 icdbow 的回复:
如果有既定的顺序为何要使用异步线程? 如果按照你上面所说,先AAA再BBB,比如有烧水是沏茶的前提这样的关系,那么使用不知道谁先结束的异步线程本身逻辑上就有问题。 而你在后边补充,像“迅雷”一样,迅雷任务都是平行的,没有先后之分,相信你有后下载的东西先下完的经历。 总之,表述前后存在矛盾,希望整理清楚思路后再重新说明问题,方便大家解决。
比如我执行一下button1_Click,那么就生成一个新线程,不管我点击多少次,如果之前的线程没有结束,界面也不要卡,拖动拉什么的都要顺畅,我就是想实现这样的功能,谢谢,也就是只要多线程+异步就OK,谢谢
jiaoshiyao 2013-09-03
  • 打赏
  • 举报
回复
引用 15 楼 dbmonths 的回复:
Control.Check....这个不安全,还是要用BeginINvoke的,谢谢
你要做什么程序。。。。
dbmonths 2013-09-03
  • 打赏
  • 举报
回复
引用 10 楼 xiaogui340 的回复:
理解,线程和异步是两回事。 你要多线程的话,BeginInvoke换成再new个线程好了。
麻烦您能给明示下吗?是UpdateTreeView方法里new新线程吗?
dbmonths 2013-09-03
  • 打赏
  • 举报
回复
引用 12 楼 jiaoshiyao 的回复:
BeginInvoke 会阻塞UI线程 你开再多的线程也没用 WinForm好想有个属性Control.Check....(check后面是什么忘了 反正是最长的)这个属性可以修改
Control.Check....这个不安全,还是要用BeginINvoke的,谢谢
加载更多回复(27)

111,092

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧