跨线程操作无效...调用和委托似乎毫无用处

时间:2018-10-30 02:49:46

标签: c# multithreading

我遇到了线程问题,我搜索了几天,但仍然无法解决。.

由于某种原因,我自定义进度表并在线程中使用它。

我试图在进度表中编写所有函数,以便它们由Invoke和委托包装。不幸的是,由于我希望this.InvokeRequired返回false时返回true,因此该代码无法正常工作。

问题是,当我执行程序时,有时会抛出异常: 跨线程操作无效:控件“ FormProgress”是从不是在其上创建线程的线程访问的。

这是进度代码表格。 我已经用Invoke和委托包装了所有功能。

public partial class FormProgress : Form
{
    public FormProgress()
    {
        InitializeComponent();
    }

    public void SetStatusLabelText(string text)
    {
        if (this.InvokeRequired)
        {
            this.Invoke((MethodInvoker) delegate
            {
                label1.Text = text;
            });
        }
        else
        {
            // exception thrown here
            label1.Text = text;
        }
    }

    public void SetDialogResult(DialogResult dialogResult)
    {
        if (this.InvokeRequired)
        {
            this.Invoke((MethodInvoker)delegate
            {
                if (DialogResult == DialogResult.None)
                    this.DialogResult = dialogResult;
            });
        }
        else
        {
            if (DialogResult == DialogResult.None)
                this.DialogResult = dialogResult;
        }
    }
}

这是线程的代码,当我单击button1时抛出异常

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        for (int i=0; i<100; i++)
            ProgressTest();
    }

    private void ProgressTest()
    {
        FormProgress dialog = new FormProgress();
        {
            Thread threadTest = new Thread(delegate ()
            {
                dialog.SetStatusLabelText("initial....(1)");
                Thread.Sleep(50);

                dialog.SetStatusLabelText("initial....(2)");
                Thread.Sleep(50);

                dialog.SetStatusLabelText("initial....(3)");
                Thread.Sleep(50);

                dialog.SetDialogResult(DialogResult.OK);

            });
            threadTest.Name = "ThreadTest";
            threadTest.Start();

            if (dialog.ShowDialog() == DialogResult.Cancel)
            {
                if (threadTest.IsAlive)
                    threadTest.Abort();
            }

            threadTest.Join();
        }
    }
}

2 个答案:

答案 0 :(得分:1)

根据the docs

  

如果控件的句柄尚不存在,InvokeRequired将向上搜索   控件的父链,直到找到执行该操作的控件或形式   有一个窗户把手。如果找不到合适的句柄,则   InvokeRequired方法返回false。

     

这意味着如果Invoke不是,则InvokeRequired可以返回false   必需(调用发生在同一线程上),或者控件是否为   在其他线程上创建,但控件的句柄尚未   已创建。

     

如果尚未创建控件的句柄,则您   不应简单地在控件上调用属性,方法或事件。   这可能导致控件的句柄在后台创建   线程,在没有消息泵的情况下隔离线程上的控件,并且   使应用程序不稳定。

     

您还可以通过检查   当InvokeRequired在背景上返回false时,为IsHandleCreated   线。如果尚未创建控制手柄,则必须等待   直到在调用Invoke或BeginInvoke之前创建它为止。   通常,仅当在   应用程序主要形式的构造函数(如   在显示表单之前,Application.Run(new MainForm())或   已调用Application.Run。

您遇到的问题是您的某些InvokeRequired调用 可能会在显示表单之前发生。这是因为您在调用dialog.ShowDialog()之前 开始新线程。 请注意,就像比赛条件一样,问题并不总是会发生-有时会出现。

如上所述,您可能需要考虑在执行IsHandleCreated块中的逻辑之前检查else(在检查InvokeRequired之后),以防止这种可能性。或者,围绕进度表重新考虑整个策略。

答案 1 :(得分:-1)

使用 if(this.InvokeRequired)块内的控件进行更改

删除在 if(this.InvokeRequired)

之后保留的else块
testfunction <- function(myVector, x)
{
uniqueCounts(myVector > x)/x
}

让我们考虑一下ProgressTest()方法,发生了什么: 在调用threadTest.Start()之后,threadTest方法开始在新线程中执行其工作项

在dialog.ShowDialog()之后,GUI线程被阻塞,这使它成为.InvokeRequired = false 同时threadTest继续工作,并且当threadTest尝试执行

public void SetStatusLabelText(string text)
{
    if (this.InvokeRequired)
    {
        this.Invoke((MethodInvoker) delegate
        {
            label1.Text = text;
        });
    }
}

public void SetDialogResult(DialogResult dialogResult)
{
    if (this.InvokeRequired)
    {
        this.Invoke((MethodInvoker)delegate
        {
            if (DialogResult == DialogResult.None)
                this.DialogResult = dialogResult;
        });
    }
}

label1.Text setter是从NONE GUI线程调用的(它是从“ ThreadTest”线程调用的),这就是为什么出现异常

应注意的是,应该调用300次的 dialog.SetStatusLabelText(“ initial ....”)实际上会被调用,而小于300 < / p>