这段代码是如何在不同的线程中执行的?

时间:2017-09-12 18:44:22

标签: c# .net multithreading winforms

在Windows窗体项目中,我有一个按钮处理程序,可以在记事本中打开文件进行编辑。记事本关闭后,我调用函数RefreshTextBox()来解析文本文件并根据值更新TextBox。以下是打开记事本并在关闭后调用refresh方法的方法:

private void button_Click(object sender, EventArgs e)
    {
            Process p = new Process
            {
                EnableRaisingEvents = true,
                StartInfo =
                {
                    FileName = "NOTEPAD.EXE",
                    Arguments = _path,
                    WindowStyle = ProcessWindowStyle.Maximized,
                    CreateNoWindow = false
                }
            };

            p.Exited += (a, b) =>
            {
                RefreshTextBox();
                p.Dispose();
            };

            p.Start();
    }

刷新文本框的代码:

private void RefreshTextBox()
    {
        using (StreamReader reader = File.OpenText(_appSettingsPath))
        {
            string text = reader.ReadToEnd();

            // Code to parse text looking for value...

            // InvalidOperationException thrown here:
            textBox.Text = reader.Value.ToString();
        }
    }

这会抛出一个异常,尝试从创建它之外的线程更新Control。我很难理解为什么。我不会在新的任务或背景工作者或类似的事情中这样做。显然,记事本在另一个线程中运行,但刷新方法直到它的进程退出后才会被调用。

编辑:我应该补充一点,在Visual Studio(作为管理员)中进行调试时,此错误会抛出致命异常弹出窗口。它自己运行应用程序时不显示弹出窗口,要么是静默吞下,要么就不会出现异常。

3 个答案:

答案 0 :(得分:6)

根据文档,如果未设置Process SynchronizingObject,它将在系统线程池中执行exited事件以避免这种情况,并在UI线程中运行该事件处理程序,您需要将SynchronizingObject设置为Form Instance

  

当SynchronizingObject为null时,将在系统线程池的线程上调用处理Exited事件的方法。有关系统线程池的更多信息,请参阅ThreadPool

如果您设置

p.SynchronizingObject = WindowsFormName;

然后它将在同一个线程中运行,否则它将在系统线程池线程中执行,这将导致交叉线程异常。

MSDN Reference

答案 1 :(得分:0)

    private void button_Click(object sender, EventArgs e)
    {
        Process p = new Process
        {

            EnableRaisingEvents = true,
            StartInfo =
            {
                FileName = "NOTEPAD.EXE",
                Arguments = _path,
                WindowStyle = ProcessWindowStyle.Maximized,
                CreateNoWindow = false
            }
        };
        //p.SynchronizingObject = this;
        p.Exited += (a, b) =>
        {
            RefreshTextBox();
            p.Dispose();
        };

        p.Start();
    }
    private void RefreshTextBox()
    {
        using (StreamReader reader = File.OpenText(_appSettingsPath))
        {
            string text = reader.ReadToEnd();

            // Code to parse text looking for value...

            //textBox.Text = text; // reader.Value.ToString();
            threadSafeControlUpdate(textBox, text);
        }
    }
    public delegate void updateUIfunc(Control c, object v);
    public void threadSafeControlUpdate(Control c, object v)
    {
        if (this.InvokeRequired)
        {
            this.BeginInvoke(new updateUIfunc(threadSafeControlUpdate), c, v);
            return;
        }
        if (c is TextBox && v is string)
        {
            c.Text = (string)v;
        }
    }

答案 2 :(得分:0)

我建议捕获同步上下文并将RefreshTextBox调用发布到它上面。类似的东西:

 private void button_Click(object sender, EventArgs e)
{
var _synchronizationContext = WindowsFormsSynchronizationContext.Current;
            Process p = new Process
            {
                EnableRaisingEvents = true,
                StartInfo =
                {
                    FileName = "NOTEPAD.EXE",
                    Arguments = _path,
                    WindowStyle = ProcessWindowStyle.Maximized,
                    CreateNoWindow = false
                }
            };

            p.Exited += (a, b) =>
            {
                _synchronizationContext.Post(_=> RefreshTextBox(), null);
                p.Dispose();
            };

            p.Start();
    }