我遇到了线程问题,我搜索了几天,但仍然无法解决。.
由于某种原因,我自定义进度表并在线程中使用它。
我试图在进度表中编写所有函数,以便它们由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();
}
}
}
答案 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>