读书人

c#winform多线程假死(不能拖动窗口

发布时间: 2013-09-06 10:17:17 作者: rapoo

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 这个耗时的操作,就可以前移呀.这样就不会卡界面了.

读书人网 >C#

热点推荐