C#InvalidOperationException和跨线程操作

时间:2009-10-20 13:37:35

标签: c# .net visual-studio

在我的Windows窗体中,我有一个文本框和一个按钮,文本框“tb_LogBox”是多行文本框我正在尝试创建一个应该调用函数的后台工作者,即“LogTimer.DnT()”时我编译并运行它Visual Studio抛出InvalidOperationException。

我得到的实际错误 跨线程操作无效:控制'tb_LogBox'从其创建的线程以外的线程访问。以下示例代码说明了我要做的事情

private void button1_Click(object sender, EventArgs e)
{
    try
    {
        var bw = new BackgroundWorker();
        bw.DoWork += ExecuteOperations ;
        bw.RunWorkerAsync();
    }
    catch (Exception ex)
    {
        tb_LogBox.AppendText(Environment.NewLine + " =@= " + ex.Message+" "+ex.Source);
    }
}

private void ExecuteOperations(object sender, DoWorkEventArgs e)
{
    var FuncCall = new LogTimer();
    tb_LogBox.AppendText(Environment.NewLine + FuncCall.DnT()); // the line i am getting the error. on 
}

public class LogTimer
{
    public string DnT()
    {
        const string datePat = @"d/MM/yyyy";
        var dateTime = DateTime.Now();
        return dateTime.ToString(datePat);
    }
}

6 个答案:

答案 0 :(得分:4)

尝试使用begin invoke方法:

    BeginInvoke(new Action(() =>
    {
       tb_LogBox.AppendText(Environment.NewLine + FuncCall.DnT());
    }));

这比Invoke更顺畅。

答案 1 :(得分:2)

您需要将Ui更改编组到UI线程上。这可以通过使用tb_LogBox.AppendText周围的invoke / begininvoke调用来执行

在Winforms应用程序中

    this.BeginInvoke((MethodInvoker)delegate
        {
            tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
        });

在WPF应用程序中:

    this.Dispatcher.BeginInvoke(
        (Action)delegate()
        {
            tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
        });

希望这有帮助!

答案 2 :(得分:1)

在ExecuteOperations中执行此操作:

tb_LogBox.Invoke((MethodInvoker)delegate() { tb_LogBox.AppendText(...) }));

您不能使用其他线程(BackgroundWorker使用.NET线程池线程)来更改UI组件。这是您在WinForms编程中习惯的主要障碍。

答案 3 :(得分:0)

您需要在UI线程上调用控件的方法:

private void ExecuteOperations(object sender, DoWorkEventArgs e)
{
    var FuncCall = new LogTimer();
    tb_LogBox.Invoke((MethodInvoker)delegate{ 
        tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
    });
}

我不知道LogTimer的作用,但很可能你也应该在委托中创建它:

private void ExecuteOperations(object sender, DoWorkEventArgs e)
{
    tb_LogBox.Invoke((MethodInvoker)delegate{ 
        var FuncCall = new LogTimer();
        tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
    });
}

答案 4 :(得分:0)

BackgroundWorker在其自己的线程上执行,与WinForms GUI元素相关的所有操作必须在创建它们的线程上运行。它们当前使用的方式BackgroundWorker与使用ThreadPool.QueueUserWorkItem()对操作进行排队相同。要使用BackgroundWorker回传到GUI,请使用ReportProgess或在worker方法中设置DoWorkEventArgs.Result属性,并对GUI线程上的相应事件做出反应。您还可以在WinForms控件上使用Invoke / BeginInvoke直接在GUI线程上执行任意代码。在你的情况下,这意味着用以下代码替换访问tb_LogBox的行:

tb_LogBox.Invoke(new Action(() =>
    tb_LogBox.AppendText(Environment.NewLine + FuncCall.DatenTime());
));

答案 5 :(得分:0)

您无法从后台工作程序的执行线程访问主机线程。您可以使用BackgroundWorker的ReportProgress方法将信息发送到主机线程。

private void button1_Click(object sender, EventArgs e)
{
    try
    {
        var bw = new BackgroundWorker();
        bw.DoWork += ExecuteOperations;
        bw.ProgressChanged += bw_ProgressChanged;
        bw.RunWorkerAsync();
    }
    catch (Exception ex)
    {
        tb_LogBox.AppendText(Environment.NewLine + " =@= " + ex.Message + " " + ex.Source);
    }
}

private static void ExecuteOperations(object sender, DoWorkEventArgs e)
{
    var FuncCall = new LogTimer();
    string text = Environment.NewLine + FuncCall.DnT();

    (sender as BackgroundWorker).ReportProgress(0, text);
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    tb_LogBox.AppendText(e.UserState as string);
}

public class LogTimer
{
    public string DnT()
    {
        const string datePat = @"d/MM/yyyy";
        var dateTime = DateTime.Now;
        return dateTime.ToString(datePat);
    }
}