【问题标题】:C# - Closing form while a thread that creates new controls is still runningC# - 在创建新控件的线程仍在运行时关闭表单
【发布时间】:2020-12-01 18:17:50
【问题描述】:

我正在尝试在辅助线程上添加自定义控件,但是当我在线程仍在运行时关闭窗口时出现此异常:

在窗口之前不能在控件上调用 Invoke 或 BeginInvoke 句柄已创建。

我不知道得到这个异常的原因是因为一个误用的线程还是因为我在线程仍在运行时关闭了窗口。

这是我得到异常的代码:

panelWall.Invoke(new Action(() =>
            {
                postControl = new FBPostUserControl(m_LoggedInUser.Name, m_LoggedInUser.ImageNormal, post.CreatedTime);
                postControl.PostBody = post.Message;
                postControl.Image = postImage;
                postControl.Dock = DockStyle.Top;
                postControl.BringToFront();
            }));

这是我的自定义控件的代码:

public partial class FBPostUserControl : UserControl
{
    private readonly string m_UserName = string.Empty;
    private readonly Image m_UserProfileImage = null;
    private readonly DateTime? m_DatePosted = null;
    private Image m_Image = null;
    private string m_PostBody = string.Empty;

    public string UserName
    {
        get { return m_UserName; }
    }

    public DateTime? DatePosted
    {
        get { return m_DatePosted; }
    }

    public Image Image
    {
        get { return m_Image; }
        set
        {
            if (value == null)
            {
                pictureBoxImage.Visible = false;
            }
            else
            {
                pictureBoxImage.Visible = true;
                pictureBoxImage.Image = value;
                updateImageSize();
            }
        }
    }

    private void updateImageSize()
    {
        if (pictureBoxImage.Image != null)
        {
            double ratio = pictureBoxImage.Image.Width / pictureBoxImage.Image.Height;
            pictureBoxImage.Height = (int)(pictureBoxImage.Width / ratio);
            pictureBoxImage.SizeMode = PictureBoxSizeMode.Zoom;
        }
    }

    public string PostBody
    {
        get { return m_PostBody; }
        set
        {
            if (string.IsNullOrWhiteSpace(value) == false)
            {
                labelPostBody.Visible = true;
                labelPostBody.Text = value;
            }
            else
            {
                labelPostBody.Visible = false;
            }
        }
    }

    public Image UserProfileImage
    {
        get { return m_UserProfileImage; }
    }

    public FBPostUserControl(string i_Name, Image i_ProfileImage, DateTime? i_PostDate)
    {
        InitializeComponent();
        m_UserName = i_Name;
        m_UserProfileImage = i_ProfileImage;
        m_DatePosted = i_PostDate;

        refreshHeader();
    }

    private void refreshHeader()
    {
        pictureBoxUserImage.Image = m_UserProfileImage;
        labelName.Text = m_UserName;

        if (labelDate != null)
        {
            labelDate.Text = m_DatePosted.ToString();
        }
        else
        {
            labelDate.Visible = false;
        }
    }
}

【问题讨论】:

  • 90% 肯定是因为“我正在关闭窗口,而线程仍在运行。”
  • 我在尝试关闭带有活动线程的窗口时遇到了该错误。我最终使用了外行的测试 (if(this.FormClosing)return; 但我确信那里有更优雅的解决方案。
  • 您的线程继续使用该控件,即使它不再存在。当然是卡布姆。在您允许窗口关闭之前,您可以确保线程停止。 stackoverflow.com/a/1732361/17034

标签: c# multithreading winforms


【解决方案1】:

12/1/2020 编辑开始

由于 https://getandplay.github.io/2019/05/15/transfer-of-execution-rights-Task-Yield-Dispatcher-Yield/ 中提到的调度程序优先级,等待 Task.Yield 存在问题

等待 System.Windows.Threading.Dispatcher.Yield() 更安全

12/1/2020 编辑开始

首先,我没有看到您在新线程中启动操作,因为 Invoke 方法只是将操作发布到 UI 线程中的调度程序队列。

因此,您的代码中没有真正的多线程,但是在执行操作时,用户有机会发布 CLOSE FORM windows 消息,并且可以在您的下一次调用之前对其进行处理。因此,为避免出现异常,请在下次调用之前检查表单是否已关闭。

顺便说一句,我认为启动一个新线程只是为了更新图形元素并没有真正的优势,因为最后它们必须在 UI 线程中更新,而你只是在往返。

如果您有一个长图形操作并且您的目标是 NET Framework 4.5 或更高版本,则执行此操作的标准方法是等待长图形操作的异步方法,并在内部间隔等待 Task.Yield() 以提供用户有机会取消、关闭窗口等。

基本上Task.Yield()将方法延续发布到UI调度器,当它返回时,你可以检查表单,如果表单关闭则取消长操作:

    async Task LongJustGraphicsOperation()
    {
        while (true)
        {
            //do some work and give a pause
            await Task.Yield();
            if (formIsClosed) break;
        }
    }

Task.Yield() 是旧 VB doevents 的 Task 版本。

注意。检查一个winform是否关闭有点棘手Detect when a form has been closed

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-22
    • 2010-10-02
    • 1970-01-01
    • 1970-01-01
    • 2015-04-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多