winform异步线程更新UI问题(求讨论,求指教,求保养)
刚才看到有人问为了winfrom中,在大数据绑定的时候出现画面假死的状态,为了解决这个问题希望通过再开一个线程来给控件绑定数据,可是画面还是会假死。
现在看到的方法有
1.掩耳盗铃法(Control.CheckForIllegalCrossThreadCalls = false;)
- C# code
using System;using System.Threading;using System.Windows.Forms;namespace ThreadTest{ public partial class Form1 : Form { public Form1() { InitializeComponent(); Control.CheckForIllegalCrossThreadCalls = false;//这一行是关键 } private void btnSet_Click(object sender, EventArgs e) { Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue)); t.Start("Hello World"); } void SetTextBoxValue(object obj) { this.textBox1.Text = obj.ToString(); } }}
这种方法是最方便,实现最简单的方式,但是这样等于是线程之间是去了限制,子线程能够直接控制父线程的控件,会出现线程之间的冲突,不是好的实现方式
2.利用BackgroundWorker
- C# code
using System;using System.ComponentModel;using System.Windows.Forms;namespace ThreadTest{ public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void btnSet_Click(object sender, EventArgs e) { //MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString()); using (BackgroundWorker bw = new BackgroundWorker()) { bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); bw.DoWork += new DoWorkEventHandler(bw_DoWork); bw.RunWorkerAsync("Hello World"); } } void bw_DoWork(object sender, DoWorkEventArgs e) { //MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString()); e.Result = e.Argument;//这里只是简单的把参数当做结果返回,当然您也可以在这里做复杂的处理后,再返回自己想要的结果(这里的操作是在另一个线程上完成的) } void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { //这时后台线程已经完成,并返回了主线程,所以可以直接使用UI控件了 this.textBox1.Text = e.Result.ToString(); //MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString()); } } }
这种方式只能在异步结束后给控件赋值,
--即bw_DoWork是异步处理,异步处理过程中不能给控件赋值
--bw_RunWorkerCompleted方法中可以给控件赋值,可是这个过程不是异步的,如果在bw_RunWorkerCompleted中加上一个System.Threading.Thread.Sleep(3000);画面还是会假死
3.不使用BackgroundWorker,自己来写线程来实现类似于BackgroundWorker的处理
子线程给主线程成员赋值的迷惑
- C# code
// 代码中自己定义的按钮(非显示按钮) private Button testBtn = new Button(); private void button1_Click(object sender, EventArgs e) { Thread thr = new Thread(ControlParent); thr.Start(); } private void ControlParent() { Thread.Sleep(3000); // 给自己定义的按钮赋值没有问题 testBtn.Text = "eeee"; // 给画面上显示的按钮赋值,InvalidOperationException错误(这里迷糊了,子线程不能直接给主线程赋值,那么主线程是什么?) this.button1.Text = "asda";// InvalidOperationException!!!!!!!!!!! }
这里的两个按钮,同样是属于一个类,只不过一个显示在画面上,另一个只定义了没有显示,为什么一个赋值成功,另一个就是败了,求大哥大姐们来个破解之法,感激不尽。
PS:窗体的this.BeginInvoke只能不阻塞后面代码的执行,不能解决画面假死
以上描述够清楚了吧,希望坛子里的大牛们能够诲人不倦,相信不只是我迷糊,还会有不少人也在迷糊着,希望能够来人指教了,欢迎指教讨论,感激不尽
[解决办法]
this.progressBar1.Invoke(new System.Action(delegate()
{
double pro = (double)index / ((double)MaxId - (double)MinId) * 100;
this.lbprogressBar.Text = pro.ToString("N2") + "%";
if (pro >= 100)
{
pro = 100;
}
this.progressBar1.Value = Convert.ToInt32(pro);
}));
[解决办法]
- C# code
this.BeginInvoke((MethodInvoker)delegate{ this.button1.Text = "asda";});
[解决办法]
主线程就是UI线程啊。
你在非UI线程上默认是不能更新UI线程上的东西的。你需要将更新这个委托交给UI线程自己去执行。
你可以在ControlParent中这样写来更新BUTTON1
this.Invoke(()=>{button1.Text="asda";});
[解决办法]
数据集.Fill(缓存数据表, 表名);/这里会是时钟控件无法执行/ 建立连接.Close();
一般读数据库ACCESS会出现这种情况,慢的电脑会使主界面状态栏中显示时间停顿5-10秒不等,即使使用线程或一部也无法解决。图片一般使用异步也就可以了,慢的电脑也不显得慢。
[解决办法]
[code=C#][/code]
using System;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private delegate void DeleWork();//委托
private Button testBtn = new Button();
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(threadWork);
thread.IsBackground = true;
thread.Start();
}
private void threadWork()
{
DeleWork ThreadDeleWork = new DeleWork(FormDeleWork);
while (true)
{
Thread.Sleep(1000);
this.BeginInvoke(ThreadDeleWork);
}
}
private void FormDeleWork()
{
testBtn.Text = "eeee";
this.button1.Text = "asda";
textBox1.Text = testBtn.Text;
}
}
}
[解决办法]
一、子线程操作主线程的控件用委托就行;
二、把延时的工作放到委托入口之外就不会假死了。
希望对你有帮助。
[解决办法]
该UI做的就让UI去做,不该UI去做的就不要让UI去做。
如果你让UI做的事情太多,比如加载1G的图片这种巨大的操作。那UI本身不做优化的话不卡死就怪了。
[解决办法]
LZ是担心这代码段会被不同你的线程访问时冲突吧
void SetTextBoxValue(object obj)
{
this.textBox1.Text = obj.ToString();
}
这样的话你可以加把锁如:
object locker=new object();
void SetTextBoxValue(object obj)
{
lock(locker)
{
this.textBox1.Text = obj.ToString();
}
}
这样就可以只有一个线程对SetTextBoxValue进行访问了,其他要访问的线程将会等待,直到SetTextBoxValue完成
[解决办法]
简便通用的方法
- C# code
public static void CrossThreadInvoke(Control ctrl, Action action) { if (ctrl.InvokeRequired) { ctrl.Invoke(action); } else { action.Invoke(); } }
[解决办法]
try
- C# code
public class Utils { public static void CrossThreadInvoke(Control ctrl, Action action) { if (ctrl.InvokeRequired) { ctrl.Invoke(action); } else { action.Invoke(); } } }
[解决办法]
- C# code
Semaphore semaphore = new Semaphore(0, 1); LoadMainForm(semaphore ) while (!semaphore.WaitOne(TimeSpan.FromMilliseconds(40), false)) { Application.DoEvents(); } private void LoadMainForm(Semaphore semaphore ){ ThreadPool.QueueUserWorkItem(delegate{ try{ mainForm = new frmMain(); semaphore.Release(); } catch (Exception ex){ semaphore.Release(); } }); }
[解决办法]
[解决办法]
声明一个委托 delegate void aa();
-------------------------------------------------
Thread.Sleep(3000);
// 给自己定义的按钮赋值没有问题
//testBtn.Text = "eeee";
// 给画面上显示的按钮赋值,InvalidOperationException错误(这里迷糊了,子线程不能直接给主线程赋值,那么主线程是什么?)
//button1.Text = "asda";
aa bb = delegate()
{
button1.Text = "asda";// InvalidOperationException!!!!!!!!!!!
};
button1.Invoke(bb);
这样不造成界面假死
[解决办法]
不知道为什么BackgroundWorker在事件中bw_DoWork中已经执行完后,为什么还要在事件bw_RunWorkerCompleted中添加sleep(100000)呢?
bw_DoWork把要执行的全部执行完,之后通过参加传递给bw_RunWorkerCompleted,显示到界面上多好 啊?不明白你要做什么。我有使用线程实现类似BackgroundWorker的功能的代码。需要可以联系我。
[解决办法]
this.BeginInvoke和Delegate.BeginInvoke完全不是一个概念,你混淆了两者。要界面不卡,多线程后台操作,应该是用后者而不是前者。
另外.NET4.5使用async是最佳方案,功能强大且编码快速,唯一的缺点是不支持XP。
[解决办法]
[解决办法]
这个以前遇到过,大数据量加载的假死是在给textbox赋值的时候,这个是瓶颈,
不管你用什么方法加载数据,但是一次赋值给textbox的时候一定会假死,使用多少个线程都不行。
可以使用分批加载,这样界面不会假死,但是用户仍然要等待,
要做的再好一点,可以先加载一部分,等用户拉动滚动条的时候,再加载下一批数据。
你可以感觉一下word或者ue的加载,都是分批加载的。再试试记事本是一次加载的(感觉上)。
[解决办法]
又纠结于数据的“一致性”,又纠结于硬要多线程去处理它,这往往是自相矛盾的。通常我丝毫没觉得有必要“用异步多线程的语法去模拟顺序阻塞处理”。
不过如果你非常想看看异步线程是怎样阻塞自己的,可以参考帖子:http://topic.csdn.net/u/20120714/09/2f7f5cf3-f6e4-4d94-a352-24abdc6c8a2a.html?seed=1255789570&r=79128209
[解决办法]
我们使用异步编程,是因为有多个足以异步处理的、无关的事情需要并行执行。
因此异步编程的大忌就是“用异步多线程编程语法去模拟同步阻塞”,因为它的性能往往是灾难——比简单的顺序执行的性能还差劲。
因此我对c#5版本的 await 关键字有些担心,我担心许多c#初学者被它“害了”,以为模拟同步阻塞是一种很随意的多线程设计习惯。尽管 await 本身实现时其实是回调的(而不是阻塞的),但是它没有给初学者展示一个科学而合理的异步多线程编程思路,反而是带到“阴沟”里去了。
[解决办法]
多线程的意义在于异步,不是分步!