跨线程操作在BackgroundWorker中无效

时间:2015-09-16 13:04:04

标签: winforms c#-4.0 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);
    }
}

2 个答案:

答案 0 :(得分:3)

有许多不同的方法可以防止表格被冻结 例如,您可以像这样加载数据:

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_RunWorkerCompletedInvoke LoadData部分。

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

答案 1 :(得分:1)

试试这个。

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