交叉线程操作在BackgroundWorker中无效 [英] Cross thread operation not valid in BackgroundWorker
问题描述
我想在数据gridview中显示一些表单加载的数据,我想显示的数据是大量的行,当我使用后台工作进程显示我以下错误。
我的代码:
private void backgroundWorker1_DoWork(object sender,DoWorkEventArgs e)
{
try
{
FTPUtility obj = new FTPUtility();
dataGridViewRequest.DataSource = obj.ListRequestFiles();
dataGridViewRequest.Columns [0] .AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridViewRequest.Columns [1] .AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridViewRequest.Columns [2] .AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridViewResponses.DataSource = obj.ListResponseFiles();
dataGridViewResponses.Columns [0] .AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridViewResponses.Columns [1] .AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridViewResponses.Columns [2] .AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message,Error Message,MessageBoxButtons.OK,MessageBoxIcon。错误);
}
}
表单载入:
private void FormFTP_Load(object sender,EventArgs e)
{
try
{
/ /this.comboBoxRequests.SelectedIndex = 0;
backgroundWorker1.RunWorkerAsync();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message,Error Message,MessageBoxButtons.OK,MessageBoxIcon。错误);
}
}
例如,您可以加载您的数据,如下所示:
private async void Form_Load(object sender,EventArgs e)
{
//做一些初始化
await LoadData();
//做一些你需要执行的其他初始化。
}
private async任务LoadData()
{
//在此处加载您的数据
//例如
FTPUtility obj = new FTPUtility();
dataGridViewRequest.DataSource = obj.ListRequestFiles();
}
这样当运行表单时,命令按照你写的顺序运行该UI是响应式的,你不会面对使用 BackgroundWorker
或线程如跨线程操作异常的常见困难。
关键点在于使用async / await。有关详细信息,请参阅使用Async和Await进行异步编程
记住这一点,你想调用LoadData的地方,你应该这样调用:
await LoadData();
您在写这段代码的方法应该是async:
private async void RefreshButton_Click(object sender,EventArgs e)
{
await LoadData
}
EDIT For .Net 4.0
对于.Net 4.0,您可以使用 Task.Run
或 BackgroundWorker
。我建议 Task.Run
,因为它更简单,更易读。
请注意,Cross Thread Operation Exception会抛出当您从UI之外的另一个线程访问UI元素。在这种情况下,你应该使用 this.Invoke(new Action(()=> {/ * Access UI Here * /}));
BackgroundWorker方法:
private void Form1_Load(object sender,EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
//如果你把一些代码放在这里例如MessageBox.Show();
//代码将自动运行,不要等待worker完成任务。
}
private void backgroundWorker1_DoWork(object sender,DoWorkEventArgs e)
{
this.LoadData();
}
public void LoadData()
{
var ftp = new FtpUtility();
var data = ftp.ListRequestFiles();
this.Invoke(new Action(()=>
{
//设置列和其他UI东西
//设置网格数据源
this.dataGridView1 .DataSource = data;
}));
}
- 记住您以前使用过的所有地方
LoadData
,现在应该使用backgroundWorker1.RunWorkerAsync();
。 - 在工作者完成任务后执行作业,将您的作业放在
backgroundWorker1_RunWorkerCompleted
或中调用
部分
LoadData
。
Task.Run方法
private void Form1_Load(object sender,EventArgs e)
{
Task.Run(()=>
{
LoadData();
})
.ContinueWith(x =>
{
//你可以放置你想要在LoadData完成后运行的代码
//如果你在这里访问UI,你应该使用Invoke
});
//如果你把一些代码放在这里例如MessageBox.Show();
//代码将自动运行,不要等待worker完成任务。
}
public void LoadData()
{
var ftp = new FtpUtility();
var data = ftp.ListRequestFiles();
this.Invoke(new Action(()=>
{
//设置列和其他UI东西
//设置网格数据源
this.dataGridView1 .DataSource = data;
}));
}
- 记住您以前使用过的所有地方
LoadData
,现在你应该使用Task.Run(()=> {LoadData();});
>
- 如果您想在
LoadData
完成后执行作业,请将作业放在ContinueWith
或调用
部分
LoadData
。
I want to display some data on form load in a data gridview , the data which i want to display is in large number of rows , when i use background worker processor it show me the following error.
My code:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
FTPUtility obj = new FTPUtility();
dataGridViewRequest.DataSource = obj.ListRequestFiles();
dataGridViewRequest.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridViewRequest.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridViewRequest.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridViewResponses.DataSource = obj.ListResponseFiles();
dataGridViewResponses.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridViewResponses.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridViewResponses.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Form load:
private void FormFTP_Load(object sender, EventArgs e)
{
try
{
//this.comboBoxRequests.SelectedIndex = 0;
backgroundWorker1.RunWorkerAsync();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
There are many different ways to prevent the form from being freezed.
For example you can load your data like this:
private async void Form_Load(object sender, EventArgs e)
{
//do some initializations
await LoadData();
//do some other initializations that you need to perform.
}
private async Task LoadData()
{
//Load your data here
//For example
FTPUtility obj = new FTPUtility();
dataGridViewRequest.DataSource = obj.ListRequestFiles();
}
This way when running the form, the commands run in the sequence you wrote while the UI is responsive and you will not face with common difficulties of using BackgroundWorker
or threads like Cross thread operation exceptions.
The key point is in using async/await. For more information read Asynchronous Programming with Async and Await
Remember that this way, every where you want to call LoadData, you should call it this way:
await LoadData();
And the method that you write this code in, should be async:
private async void RefreshButton_Click(object sender, EventArgs e)
{
await LoadData();
}
EDIT For .Net 4.0
For .Net 4.0 you can use both Task.Run
or BackgroundWorker
. I recommend Task.Run
because it is more simple and more readable.
Please note Cross Thread Operation Exception will throw when you access the UI elements from another thread than UI. In this situations you should use this.Invoke(new Action(()=>{/*Access UI Here*/}));
instead. And never put a time-consuming task in your invoke part.
BackgroundWorker Approach:
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
//If you put some code here for example MessageBox.Show("");
//The code will immadiately run and don't wait for worker to complete the task.
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
this.LoadData();
}
public void LoadData()
{
var ftp = new FtpUtility();
var data = ftp.ListRequestFiles();
this.Invoke(new Action(() =>
{
//Setup columns and other UI stuff
//Set datasource of grid
this.dataGridView1.DataSource = data;
}));
}
- Remember everywhere you formerly used
LoadData
, now you should usebackgroundWorker1.RunWorkerAsync();
instead. - If you want to do a job after the worker completes the task, put your job in
backgroundWorker1_RunWorkerCompleted
or inInvoke
part ofLoadData
.
Task.Run Approach
private void Form1_Load(object sender, EventArgs e)
{
Task.Run(() =>
{
LoadData();
})
.ContinueWith(x =>
{
//You can put codes you want run after LoadData completed here
//If you access the UI here, you should use Invoke
});
//If you put some code here for example MessageBox.Show("");
//The code will immadiately run and don't wait for worker to complete the task.
}
public void LoadData()
{
var ftp = new FtpUtility();
var data = ftp.ListRequestFiles();
this.Invoke(new Action(() =>
{
//Setup columns and other UI stuff
//Set datasource of grid
this.dataGridView1.DataSource = data;
}));
}
- Remember everywhere you formerly used
LoadData
, now you should useTask.Run(()=>{LoadData();});
instead. - If you want to do a job after the
LoadData
completes, put your job inContinueWith
or inInvoke
part ofLoadData
.
这篇关于交叉线程操作在BackgroundWorker中无效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!