跨线程操作在 BackgroundWorker 中无效
Cross thread operation not valid in BackgroundWorker
我想在数据网格视图中显示有关表单加载的一些数据,我想显示的数据有很多行,当我使用后台工作处理器时,它显示以下错误。
我的代码:
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);
}
}
表单加载:
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);
}
}
有许多不同的方法可以防止表格被冻结。
例如,您可以像这样加载数据:
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();
}
这样,当 运行 填写表单时,命令 运行 按照您编写的顺序排列,而 UI 是响应式的,您将不会遇到使用 BackgroundWorker
或线程之类的跨线程操作异常。
关键在于使用async/await。有关详细信息,请阅读 Asynchronous Programming with Async and Await
记住这样,每一个你想调用LoadData的地方,你都应该这样调用:
await LoadData();
您编写此代码的方法应该是异步的:
private async void RefreshButton_Click(object sender, EventArgs e)
{
await LoadData();
}
编辑.Net 4.0
对于 .Net 4.0,您可以同时使用 Task.Run
或 BackgroundWorker
。我推荐Task.Run
,因为它更简单,更易读。
请注意,当您从 UI 以外的另一个线程访问 UI 元素时,将抛出跨线程操作异常。在这种情况下,您应该改用 this.Invoke(new Action(()=>{/*Access UI Here*/}));
。永远不要在调用部分放置耗时的任务。
BackgroundWorker 方法:
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;
}));
}
- 记住你以前在任何地方使用
LoadData
,现在你应该使用backgroundWorker1.RunWorkerAsync();
。
- 如果您想在工人完成任务后做工作,请将您的工作放在
backgroundWorker1_RunWorkerCompleted
或LoadData
的Invoke
部分。
Task.Run进场
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;
}));
}
- 记住你以前在任何地方使用
LoadData
,现在你应该使用Task.Run(()=>{LoadData();});
。
- 如果您想在
LoadData
完成后完成工作,请将您的工作放在 ContinueWith
或 LoadData
的 Invoke
部分。
试试这个。
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
dataGridViewRequest.Invoke(new Action(() => {
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);
}
}
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);
}
}
我想在数据网格视图中显示有关表单加载的一些数据,我想显示的数据有很多行,当我使用后台工作处理器时,它显示以下错误。
我的代码:
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);
}
}
表单加载:
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);
}
}
有许多不同的方法可以防止表格被冻结。
例如,您可以像这样加载数据:
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();
}
这样,当 运行 填写表单时,命令 运行 按照您编写的顺序排列,而 UI 是响应式的,您将不会遇到使用 BackgroundWorker
或线程之类的跨线程操作异常。
关键在于使用async/await。有关详细信息,请阅读 Asynchronous Programming with Async and Await
记住这样,每一个你想调用LoadData的地方,你都应该这样调用:
await LoadData();
您编写此代码的方法应该是异步的:
private async void RefreshButton_Click(object sender, EventArgs e)
{
await LoadData();
}
编辑.Net 4.0
对于 .Net 4.0,您可以同时使用 Task.Run
或 BackgroundWorker
。我推荐Task.Run
,因为它更简单,更易读。
请注意,当您从 UI 以外的另一个线程访问 UI 元素时,将抛出跨线程操作异常。在这种情况下,您应该改用 this.Invoke(new Action(()=>{/*Access UI Here*/}));
。永远不要在调用部分放置耗时的任务。
BackgroundWorker 方法:
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;
}));
}
- 记住你以前在任何地方使用
LoadData
,现在你应该使用backgroundWorker1.RunWorkerAsync();
。 - 如果您想在工人完成任务后做工作,请将您的工作放在
backgroundWorker1_RunWorkerCompleted
或LoadData
的Invoke
部分。
Task.Run进场
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;
}));
}
- 记住你以前在任何地方使用
LoadData
,现在你应该使用Task.Run(()=>{LoadData();});
。 - 如果您想在
LoadData
完成后完成工作,请将您的工作放在ContinueWith
或LoadData
的Invoke
部分。
试试这个。
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
dataGridViewRequest.Invoke(new Action(() => {
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);
}
}
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);
}
}