我有一个带有datagridview和一些按钮的窗体。单击其中一个按钮将调用名为loadMyData()
的方法,该方法从csv中读取一些数据并将其放入表单中的三个datagridviews中。
代码是这样的:
public partial class NewForm : Form
{
private void loadData_Click_1(object sender, EventArgs e) // load market data, create a base copy and update gridview
{
ThreadStart thread1Start = new ThreadStart(loadMyData);
Thread t1 = new Thread(thread1Start);
t1.Start();
}
public void loadMyData()
{
dataMap = dataLoader.newLoadTheData(dataMap, grid1, grid2)
}
}
其中dataLoader.newLoadTheData
是一个静态方法,它将datagridviews(grid1,grid2)和字典(dataMap)作为输入。该方法只是从csv读取一些数据并将数字放在2个datagridviews中。这些是从此方法更新的,并且该方法也返回更新的字典(dataMap)。当方法loadMyData()
正常执行时,一切正常,但当我以线程执行它时出现此错误:
跨线程操作无效:控制' grid1'从a访问 除了创建它的线程以外的线程。
我意识到我可能正在使用类似"调用"但我真的无法找到一个明确的例子来说明如何在我的案例中做到这一点。任何人都可以帮助解决这个问题吗?我应该如何更改代码才能使其正常工作?
答案 0 :(得分:3)
从其他线程处理网格时,您应该执行以下操作:
if (grid1.InvokeRequired)
grid1.Invoke(new Action(() => { /*do my stuff here*/ })
else
{
/*do my stuff here*/
}
答案 1 :(得分:0)
System.Threading.Tasks 可让您轻松创建子任务,并在完成后运行完成块。如果您指定UI上下文,那么完成块将在UI线程中运行,不需要Invoke()
。
代码:
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(
() =>
{
// this runs in worker thread
loadMyData();
DoSomeLengthyJob();
DoSomethingElse();
})
.ContinueWith(t =>
{
// now we are in UI thread
// now update the UI with whatever you want
// with the results from your worker thread
dataGridView1.Rows.Add();
}, ui);
答案 2 :(得分:-1)
您必须将回调编组回UI线程。
您使用的是WinForms还是WPF?
在WPF中,您可以使用Dispatcher。
在WinForms中:
尝试
// Get the UI thread's context in the constructor.
var context = TaskScheduler.FromCurrentSynchronizationContext();
// Then its possible to start a task directly on the UI thread
var token = Task.Factory.CancellationToken;
Task.Factory.StartNew(() =>
{
this.label1.Text = "Task past first work section...";
}, token, TaskCreationOptions.None, context);
编辑:
您收到此错误的原因是您尝试从另一个线程访问Grid控件。通常,大多数UI应用程序都在STA(单线程亲和力)模型中,其中与UI的任何交互必须在“UI”线程上完成,该线程通常是应用程序启动的主要/第一个线程。
当您在后台主题上加载数据时,在完成之后,您需要一种方法来 Marshal (调用/运行)更新网格的代码在主要/用户界面主题。
要实现这一点,您可以使用当前的SynchronizationContext在主线程上创建TaskScheduler(如在窗口/控件的构造函数中,当前上下文将是UI线程),然后您可以将该上下文传递给Task .Factory.StartNew方法作为参数,以便它知道“Marshal”(调用/运行)给定“上下文”的代码,即UI线程