我有这种奇怪的情况,我试图从中调用控件的方法 一个线程。这是非常简单的代码,但由于某种原因,如果InvokeRequired为true,则调用永远不会执行。它永远挂起,没有异常抛出。 下面是一个简化版本,一旦控件添加到父窗体,它也会挂起。如果它没有添加到表单中,则InvokeRequired为false并且工作正常。
MyControl _ctl;
Thread _thread;
void Main()
{
_ctl = new MyControl
{
Dock = DockStyle.Fill
};
Form frm = new Form();
frm.Controls.Add(_ctl);
frm.Show();
(_thread = new Thread(Test)).Start();
//Thread.Sleep(2000);
_thread.Join();
}
void Test()
{
_ctl.Write("Hello...!");
}
class MyControl : TextBox
{
int _inputStart = -1;
public void Write(string value)
{
Action act = () =>
{
AppendText(value);
_inputStart = SelectionStart;
};
if (InvokeRequired)
Invoke(act, Array.Empty<object>()); // this is where line execution hangs
else
act();
}
}
我想了解它为什么会挂起来?以及实施此类方案的正确方法是什么?
修改 好的,为了回应@Sami Kuhmonen的评论,我制作了另一个示例代码作为WinForm小应用程序运行
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
namespace testThreads
{
public class Form1 : Form
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components;
private MyControl _ctl;
private Button _button;
private Thread _thread;
public Form1()
{
InitializeComponent();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
components?.Dispose();
components = null;
}
base.Dispose(disposing);
}
private void TestThreading()
{
(_thread = new Thread(Test)).Start();
_thread.Join();
}
void Test()
{
_ctl.Write("Hello...!");
}
private void InitializeComponent()
{
SuspendLayout();
//
// _ctl
//
_ctl = new MyControl
{
Multiline = true,
Location = Point.Empty,
Size = new Size(300, 175)
};
Controls.Add(_ctl);
//
// _button
//
_button = new Button
{
Location = new Point(0, 177),
Size = new Size(60, 21),
Text = "Test"
};
Controls.Add(_button);
_button.Click += (s, e) => TestThreading();
//
// Form1
//
ClientSize = new Size(300, 200);
Name = "Form1";
Text = "Form1";
ResumeLayout(false);
}
}
class MyControl : TextBox
{
int _inputStart = -1;
public void Write(string value)
{
Action act = () =>
{
AppendText(value);
_inputStart = SelectionStart;
};
if (InvokeRequired)
Invoke(act, Array.Empty<object>());
else
act();
}
}
}
现在,如果我不使用_thread.Join();代码执行正常。但在我的真实场景中,我并没有使用Thread.Join,而是等待WaitHandle,它是这样的:
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
namespace testThreads
{
public class Form1 : Form
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components;
private MyControl _ctl;
private Button _button;
private Thread _thread;
private readonly AutoResetEvent _evt = new AutoResetEvent(false);
public Form1()
{
InitializeComponent();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
components?.Dispose();
components = null;
}
base.Dispose(disposing);
}
private void TestThreading()
{
(_thread = new Thread(Test)).Start();
_evt.WaitOne();
}
void Test()
{
_ctl.Write("Hello...!");
// simulate a long running task
Thread.Sleep(2000);
_ctl.Write("Done");
_evt.Set();
}
private void InitializeComponent()
{
SuspendLayout();
//
// _ctl
//
_ctl = new MyControl
{
Multiline = true,
Location = Point.Empty,
Size = new Size(300, 175)
};
Controls.Add(_ctl);
//
// _button
//
_button = new Button
{
Location = new Point(0, 177),
Size = new Size(60, 21),
Text = "Test"
};
Controls.Add(_button);
_button.Click += (s, e) => TestThreading();
//
// Form1
//
ClientSize = new Size(300, 200);
Name = "Form1";
Text = "Form1";
ResumeLayout(false);
}
}
class MyControl : TextBox
{
int _inputStart = -1;
public void Write(string value)
{
Action act = () =>
{
AppendText(value);
_inputStart = SelectionStart;
};
if (InvokeRequired)
Invoke(act, Array.Empty<object>());// hangs
else
act();
}
}
}
当然,这是一个样本,它不会是一个单一的线程。
所以我想我需要阻止并等到执行完毕,同时更新控件的UI。或者,如果可能存在其他非阻塞方法..