我要去做一个很大的假设(所有线索都指向那个方向):
我假设您在 Windows 窗体中执行所有这些操作,并且您希望 GUI 在 Thread.Sleep() 调用期间不要冻结,对吗?如果是这种情况,那么您可以编写此方法:
public static class Foo {
public static void MySleep(int milliseconds) {
int startTick = Environment.TickCount;
while (Environment.TickCount - startTick < milliseconds)
Application.DoEvents();
}
}
然后用 Foo.MySleep 调用替换 Thread.Sleep 调用(但要注意 Application.DoEvents 的含义,它允许在控件位于任何 MySleep 调用内部时重新执行整个过程。我不推荐如果您不采取预防措施,就以这种方式做事——例如,您应该在测试执行时始终禁用调用func test()的按钮,因为它可以在永无止境的递归中一次又一次地执行):
func test()
for(int i=0;i<100;i++){
func A()
Foo.MySleep(20 * 1000);
func B()
Foo.MySleep(45 * 1000);
func C()
Foo.MySleep(2 * 60 * 1000);
}//repeat for 100 times
还有另一种方法(更安全一点)可以做到这一点。它涉及辅助线程和一些 Control.Invoke 调用。它还允许您使用标准 System.Thread.Sleep(int milliseconds) 方法。
它是这样的:
public class Form1 : Form {
public void triggerTestAndReturnImmediately() {
new Thread(start: () => {
Action callA = () => funcA();
Action callB = () => funcB();
Action callC = () => funcC();
for(int i=0;i<100;i++){
this.Invoke(callA);
Thread.Sleep(20 * 1000);
this.Invoke(callB);
Thread.Sleep(45 * 1000);
this.Invoke(callC);
Thread.Sleep(2 * 60 * 1000);
}//repeat for 100 times
}).Start();
}
}
“this.Invoke(someDelegate)”位基于“triggerTestAndReturnImmediately”方法是 Form 子类的成员而“this”是 Form 实例这一事实。
这里发生的是您在单独的线程上运行等待操作 + 触发实际工作人员(funcA、funcB、funcC)。为什么你只运行实际工人的触发而不是实际工人本身?因为您很可能会从这些函数访问 GUI 对象,而这在 WinForms(以及许多其他 .NET 或非 .NET GUI 框架)中是不允许的。
因此,您要求 GUI 线程(正如您在问题中指出的那样)必须在 99% 的时间内空闲才能执行这些功能 - 因此是 this.Invoke ( someDelegate )调用。
您应该采取与我之前提到的相同的预防措施,但这一切都取决于您以及您希望您的应用程序做什么。假设您想要一个按钮的 Click 事件处理程序调用triggerTestAndReturnImmediately并且您希望它在此过程中被禁用,您将如何禁用和重新启用该按钮?
如果你这样做:
void buttonBar_Click(object sender, EventArgs e) {
this.buttonBar.Enabled = false;
this.triggerTestAndReturnImmediately();
this.buttonBar.Enabled = true;
}
那么这对你没有任何好处,因为triggerTestAndReturnImmediately方法正如它的名字所暗示的那样:“它立即返回”。
那么你将如何解决这个问题?您可以执行以下操作:
void buttonBar_Click(object sender, EventArgs e) {
this.triggerTestAndReturnImmediately();
}
public void triggerTestAndReturnImmediately() {
this.buttonBar.Enabled = false;
new Thread(start: () => {
Action callA = () => funcA();
Action callB = () => funcB();
Action callC = () => funcC();
Action reenableButton = () => this.buttonBar.Enabled = true;
for(int i=0;i<100;i++){
this.Invoke(callA);
Thread.Sleep(20 * 1000);
this.Invoke(callB);
Thread.Sleep(45 * 1000);
this.Invoke(callC);
Thread.Sleep(2 * 60 * 1000);
this.Invoke(reenableButton);
}//repeat for 100 times
}).Start();
}
还要确保您始终重新启用按钮,无论调用序列是否因某些try finally语句或其他体操而崩溃。
所以我认为您可以了解我所说的预防措施是什么(它们取决于您希望您的应用程序做什么)。