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

C# 这段代码是如何在不同的线程中执行的?,c#,.net,multithreading,winforms,C#,.net,Multithreading,Winforms,在Windows窗体项目中,我有一个按钮处理程序,用于在记事本中打开文件进行编辑。记事本关闭后,我调用函数refreshttextbox(),解析文本文件并根据值更新文本框。以下是打开记事本并在其关闭后调用刷新方法的方法: private void button_Click(object sender, EventArgs e) { Process p = new Process { EnableRaisin

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

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();
        }
    }
如果试图从创建控件的线程以外的线程更新该控件,则会引发异常。我很难理解为什么。我不是在一个新的任务或后台工作人员或诸如此类的工作中这样做的。显然,记事本正在另一个线程中运行,但刷新方法直到其进程退出后才被调用


编辑:我应该补充一点,这个错误在VisualStudio中调试时(作为管理员)会弹出一个致命的异常。单独运行应用程序时,不会显示弹出窗口,异常会被默默地吞没,或者不会发生。

根据文档,如果未设置进程
同步对象
,它将在系统线程池中执行exited事件以避免此情况,并在UI线程中运行该事件处理程序。您需要设置
同步对象
,以形成实例

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

如果你设定

p.SynchronizingObject = WindowsFormName;
然后它将在同一个线程中运行,或者在系统线程池线程中执行,这将导致交叉线程异常


我建议捕获同步上下文并将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();
    }

Exited
事件可能是在一个单独的线程中引发的-这是我所期望的,否则在许多情况下,您永远不会看到它。(想象一下,一个控制台应用程序没有像Windows窗体线程那样的“空闲”状态。)下面是一篇关于如何从另一个线程更新UI的文章,这将帮助您解决问题。您可以使用p.SynchronizingObject将其强制放在与sumeet kumar指示的正常UI更新兼容的线程中,但如果您希望它在单独的线程中运行,则可以使用委托从另一个线程更新控件。我使用一个通用的threadSafeControlUpdate来确定控件的类型和传递给更新的数据的类型。我只复制了文本框代码,但我的普通threadSafeControlUpdate()处理pictureBox、DataGridView等。。。
 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();
    }