先看一个很简单的例子:一个主Form,TestForm2,在载入的时候Show另一个Form (TestForm3):
如此之简单,会有什么问题呢?而我的问题是:主Form和它所Show出的另一个Form是什么关系呢?目前的情况是,当我们关闭主Form里,副Form是一定会跟着关闭的,这或许是天经地义的,因为“父窗口”关闭后,子窗口一定得关闭。(暂时定义它们为父子窗口关系吧)
然而问题是,如果在TestForm里添加一个阻塞线程的函数,那么在主Form退出的时候,子Form没有能一起退出,接下来的问题就是,再退出子Form的时候就会让主进程死在了内存里。
两个Form的简单代码:
private void TestForm2_Load(object sender, System.EventArgs e)
{
this.label1.Text = "Main Form, and ID is:"+this.Handle.ToString();
}
private void button1_Click(object sender, System.EventArgs e)
{
TestForm3 m_form = new TestForm3();
m_form.Show();
}
{
this.label1.Text = "Main Form, and ID is:"+this.Handle.ToString();
}
private void button1_Click(object sender, System.EventArgs e)
{
TestForm3 m_form = new TestForm3();
m_form.Show();
}
private void TestForm3_Load(object sender, System.EventArgs e)
{
this.label1.Text = "Child form and ID is:"+this.Handle.ToString();
}
private void button1_Click(object sender, System.EventArgs e)
{
MessageBox.Show("This is a test.");
}
注意操作关系:{
this.label1.Text = "Child form and ID is:"+this.Handle.ToString();
}
private void button1_Click(object sender, System.EventArgs e)
{
MessageBox.Show("This is a test.");
}
1、让主Form(TestForm2)Show一个新的Form(TestForm3)出来。
2、让新Show出来的TestForm3再Show一个MessageBox来阻塞线程。
3、退出主Form也就是TestFrom2,此时TestForm3被MessageBox阻塞。
4、再退出TestForm3,然后观察任务管理器就会发现应该程序的主进程已经死在里面了。
分析原因:这两个Form一定是多线程关系:
首先,它们在任务栏里显示的是多个窗口。其次,TestForm3被阻塞的时候,TestForm2可以响应用户操作,最后就是退出时再现的这一现象。
然而事情也还没有那么简单,如果主Form所Show也来的窗口是一个新的线程,那么我再次Show的时候,应该会产生新的线程,结果是:在任务管理器里,只有3个,不管我再Show多少个窗口出来。。。。。
而且:System.Threading.Thread.CurrentThread.GetHashCode()所得到的主从窗口是一样的。。。这让我很费解。
用Syp++查看,结果也让人很郁闷:所有的Form都是同一个进程ID也是同一个线程ID!既然事实是这样的,就没什么好再分析的,接下来的问题是:一个进程,一个线程,它是怎样工作的呢?为什么可以Show一个子Form而不影响主Form呢?也就是说它们完全主像是一个多线程!
猜测单线程下多Form的工作原理:
因为只有这样才能很好的解释所以遇到的问题:
因为主线程首先响应主Form,所以它首先得到用户响应。而其它的子Form就会以并列的方式处理。而子Form里的MessageBox的优先级比所有的子Form高,所以在子Form中(不管是哪一个)只要有一个MessageBox出现,它就会阻塞所有的子Form,这与事实相符。
同样的,根据上面的图示,如果我在主Form里也Show一个MessageBox那么所有的Form都会被阻塞。而实际与是如此。因此这样的工作模式可以解释所遇到的所有问题。
测试代码:
Form2
private void TestForm2_Load(object sender, System.EventArgs e)
{
this.label1.Text = "Main Form, and ID is:"+this.Handle.ToString();
this.label2.Text = "ThreadID:"+System.Threading.Thread.CurrentThread.GetHashCode().ToString();
}
private void button1_Click(object sender, System.EventArgs e)
{
TestForm3 m_form = new TestForm3();
m_form.Show();
}
private void button2_Click(object sender, System.EventArgs e)
{
MessageBox.Show("This is a test!");
}
{
this.label1.Text = "Main Form, and ID is:"+this.Handle.ToString();
this.label2.Text = "ThreadID:"+System.Threading.Thread.CurrentThread.GetHashCode().ToString();
}
private void button1_Click(object sender, System.EventArgs e)
{
TestForm3 m_form = new TestForm3();
m_form.Show();
}
private void button2_Click(object sender, System.EventArgs e)
{
MessageBox.Show("This is a test!");
}
Form3
private void TestForm3_Load(object sender, System.EventArgs e)
{
this.label1.Text = "Child form and ID is:"+this.Handle.ToString();
this.label2.Text = "ThreadID:"+System.Threading.Thread.CurrentThread.GetHashCode().ToString();
}
private void button1_Click(object sender, System.EventArgs e)
{
MessageBox.Show("This is a test.");
}
{
this.label1.Text = "Child form and ID is:"+this.Handle.ToString();
this.label2.Text = "ThreadID:"+System.Threading.Thread.CurrentThread.GetHashCode().ToString();
}
private void button1_Click(object sender, System.EventArgs e)
{
MessageBox.Show("This is a test.");
}
总结:这并不是一个好的方法来Show窗口,因为这样很让人觉得它是多线程的是,特别是在任务栏上的多窗口,以及MessageBox的阻塞等都很像多线程。。然而事实并非如此。
这是我偶然遇到的一个问题,实际上如果要处理多窗口,还是应该用多线程,而且也不应该只在一个函数里Show而是应该在全局上处理一下。