跨线程操作在 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.RunBackgroundWorker。我推荐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_RunWorkerCompletedLoadDataInvoke部分。

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 完成后完成工作,请将您的工作放在 ContinueWithLoadDataInvoke 部分。

试试这个。

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);
    }
}