首页 文章

C#InvalidOperationException和跨线程操作[重复]

提问于
浏览
0

这个问题在这里已有答案:

在我的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 回答

  • 2

    尝试使用begin invoke方法:

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

    这比Invoke更顺畅 .

  • 1

    你需要将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());
            });
    

    希望这可以帮助!

  • 0

    在ExecuteOperations中执行此操作:

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

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

  • 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

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

相关问题