如何跨线程调用方法?多线程绘图报错的Invoke实现咨询
解决WinForms跨线程访问图表控件的问题
你遇到的是WinForms里最常见的跨线程操作UI错误——所有UI控件(包括你的chart1)都只能在**创建它们的主线程(也就是UI线程)**上修改,后台线程直接碰肯定会报错。Invoke确实是正确的解决方案,它的作用就是把你要执行的UI操作“委托”给UI线程去处理,我给你一步步讲具体怎么实现。
先修正你的单图表单线程代码
假设你原来的代码大概是这样(补全你没写完的部分):
private void button1_Click(object sender, EventArgs e) { Thread th = new Thread(thread); th.Start(); } public void graph(List<double> xlist, List<double> ylist) { // 这里直接操作chart1,会触发跨线程错误 chart1.Series[0].Points.DataBindXY(xlist, ylist); } private void thread() { // 模拟耗时的数据计算 List<double> x = Enumerable.Range(0, 100).Select(i => (double)i).ToList(); List<double> y = x.Select(i => Math.Sin(i / 10.0)).ToList(); graph(x, y); // 直接调用就会炸 }
方法一:在图表操作方法里加Invoke判断
最简单的修改方式,就是在graph方法开头先检查当前线程是不是UI线程,如果不是,就通过Invoke把自己再调用一遍(这时候就是UI线程执行了):
public void graph(List<double> xlist, List<double> ylist) { // 判断是否需要跨线程调用 if (chart1.InvokeRequired) { // 创建委托,把当前方法和参数传给UI线程执行 chart1.Invoke(new Action<List<double>, List<double>>(graph), xlist, ylist); return; // 委托已经发出去了,当前线程不用再往下走 } // 到这里就是UI线程了,安全操作chart1 chart1.Series[0].Points.Clear(); chart1.Series[0].Points.DataBindXY(xlist, ylist); }
方法二:用Lambda简化Invoke(.NET 4.0+适用)
如果你觉得定义委托麻烦,也可以直接用Lambda表达式,代码更简洁:
private void thread() { // 耗时计算还是放后台线程 List<double> x = Enumerable.Range(0, 100).Select(i => (double)i).ToList(); List<double> y = x.Select(i => Math.Sin(i / 10.0)).ToList(); // 直接把UI操作包在Invoke里 chart1.Invoke((Action)(() => { chart1.Series[0].Points.Clear(); chart1.Series[0].Points.DataBindXY(x, y); })); }
扩展到多图表多线程的情况
如果要同时更新两个图表,原理完全一样——每个图表的操作都要通过Invoke交给UI线程。你可以写一个通用的更新方法,避免重复代码:
private void button1_Click(object sender, EventArgs e) { // 启动两个独立线程分别处理两个图表的数据 Thread thChart1 = new Thread(UpdateChart1Data); thChart1.Start(); Thread thChart2 = new Thread(UpdateChart2Data); thChart2.Start(); } // 处理chart1的耗时数据计算 private void UpdateChart1Data() { List<double> x = Enumerable.Range(0, 100).Select(i => (double)i).ToList(); List<double> y = x.Select(i => Math.Sin(i / 10.0)).ToList(); // 调用通用更新方法,通过窗体的Invoke(和控件Invoke效果一样) this.Invoke((Action)(() => UpdateChart(chart1, x, y))); } // 处理chart2的耗时数据计算 private void UpdateChart2Data() { List<double> x = Enumerable.Range(0, 100).Select(i => (double)i).ToList(); List<double> y = x.Select(i => Math.Cos(i / 10.0)).ToList(); this.Invoke((Action)(() => UpdateChart(chart2, x, y))); } // 通用的UI线程更新方法,接收目标图表和数据 private void UpdateChart(Chart targetChart, List<double> xlist, List<double> ylist) { targetChart.Series[0].Points.Clear(); targetChart.Series[0].Points.DataBindXY(xlist, ylist); }
这样两个后台线程各自计算数据,然后把更新操作委托给UI线程,就不会再出现跨线程错误了。
内容的提问来源于stack exchange,提问作者Rob
相关产品推荐
相关产品推荐

