转:http://www.cnblogs.com/bluewater/archive/2007/06/13/781708.html


下面说明了五种可以实现等待窗体的方式,其中三种给出了代码。

准备资料

安全访问控件成员

为了保证在创建控件的线程上调用控件成员,用下面的方式封装控件的属性、方法、其他自定义成员的访问。

如: winWordControl.LoadDocument()封装为:

delegate void VoidDelegate();  

private void LoadDocument()

        {

            if (InvokeRequired)

            {

                Invoke(new VoidDelegate(LoadDocument));

                return;

            }

            this.winWordControl.LoadDocument();      

        }

下面的代码封装了winWordControl.OpenMode=Opration

private delegate void OpenModeDelegate(Operation op);

        private void OpenMode(Operation value)

        {

            if (InvokeRequired)

            {

                Invoke(new OpenModeDelegate(OpenMode), new object[] { value });

                return;

 

            }

            winWordControl.OpenMode = value;

      

        }

这些封装保证了对控件成员的访问在任何线程都是安全的。

例子说明

例子假定有三个窗体:

MainForm:

Preview preview;//Preview 是在此线程创建

Privite void SomeMethod()

{

Thread wordInit = new Thread(new ThreadStart(preview.InitWordDocControl));

     wordInit.Start();

}

第二个窗体

PreviewForm:

WinWordControl winWordControl = new WinWordControl();

Private void InitWordDocControl()

{

     //执行初始化加载word、实现延迟窗体

     //TODO

}

第三个窗体

WaitForm:提示信息

实现方式

InitWordDocControl为了在加载word时不出现假死,必须开启新线程。
由于用到了非托管资源,最简单的方式是把托管资源(WaitForm)放在工作线程,线程结束,窗体会自动销毁,不用自己写清理代码。

第一种:ShowDialog

ShowDialog自动阻塞当前线程,这使它成为最优的解决方式。

Private void InitWordDocControl()

{

     Thread thread = new Thread(new ThreadStart(Waiting));

     thread.Start();

     LoadDocument();

thread.Abort();//销毁线程,自动回收托管资源

}

  private void Waiting()

        {

          //局部变量,在此线程创建,可以直接操作其成员

            Wait FormWait = new Wait();

            FormWait.StartPosition = FormStartPosition.CenterScreen;

            FormWait.ShowDialog(); //线程等待         

        }

private void LoadDocument()

        {

            if (InvokeRequired)

            {

                Invoke(new VoidDelegate(LoadDocument));

                return;

            }

            this.winWordControl.LoadDocument();       

        }

最简单的解决方式,利用了托管资源的优势和ShowDialog本身的特性。

第二种:Show

如果简单的修改Waiting为:

  private void Waiting()

        {

           Wait FormWait = new Wait();

            FormWait.StartPosition = FormStartPosition.CenterScreen;

            FormWait.Show();       //窗体立即被销毁 

        }

窗体肯定会一闪而过,因为FormWait是局部变量,出了方法体就会被回收。

因此要改成下面的形式:

首先把局部变量改为字段,让他在加载类型时分配内存。其次,做如此修改后,创建Wait的线程就变成了创建Preview的线程,这样就不能直接修改此窗体属性,必须用Invoke。

private void Waiting()

        {

//下面的调用都是线程安全的,内部都会判断是否是创建线程,不是会调用Invoke

            CreateWait();

            SetFormStartPosition(FormStartPosition.CenterScreen);

            ShowWait();

        }

修改:

Private void InitWordDocControl()

{

     Thread thread = new Thread(new ThreadStart(Waiting));

     thread.Start();

     thread.Join();//阻塞调用线程,让其先执行完show

     LoadDocument();

}

修改LoadDocument()

private void LoadDocument()

        {

            if (InvokeRequired)

            {

                Invoke(new VoidDelegate(LoadDocument));

                return;

            }

            this.winWordControl.LoadDocument(); 

            CloseWait(); //释放资源,线程自动销毁。注意也要使用线程安全的形式。    

        }

第三种:异步委托

本质上通过线程池中的线程执行委托方法,仍然是线程问题。但是可以用show和异步委托结合,简单的实现等待。可以看出代码比上面实现简单许多。

首先这次把加载word放到新线程中,而WaitForm在原线程。

修改:

Private void InitWordDocControl()

{

     Waiting();

     MethodInvoker mi = new MethodInvoker(LoadDocument);

     mi.BeginInvoke(AsyncCallClose,null);//执行完委托方法,执行AsyncCallClose来关闭等待窗体。

}

private void AsyncCallClose(IAsyncResult ar)

        {

            FormWait.Close();             

        }

 MethodInvoker,只是系统定义的委托,提供些许便利,更好的方式是自己定义如:

delegate void VoidDelegate();VoidDelegate 和MethodInvoker是等价的。

在这种实现中发现了下面的问题:控件成员可以不在创建控件的线程中使用!!如FormWait.Close();调用此语句的是线程池中的线程,而FormWait是在另外的线程中创建。不知道是什么原因??按照必须在创建控件线程调用成员,则会抛异常。按照许多资料,这种情况也是非法调用,但在此却没有问题?!

第四种:信号量

以前没有用过线程,遇到这个问题,首先想到的是信号量来控制线程通信。可以参考后面的资料或者我以前的文章:

http://www.cnblogs.com/bluewater/archive/2006/08/14/476720.html

第五种:Timer

本质也是线程,有同学说,他一直在VB中用Timer来模拟线程来控制时间片。应该也能实现等待窗体。

在此使用线程,主要是为了有好的用户体验,避免假死。

第一次使用线程,查了许多资料,但仍然有个问题没有解决,再写一遍,希望有人指点原因。

在这种实现中发现了下面的问题:控件成员可以不在创建控件的线程中使用!!如FormWait.Close();调用此语句的是线程池中的线程,而FormWait是在另外的线程中创建。不知道是什么原因??按照必须在创建控件线程调用成员,则会抛异常。按照许多资料,这种情况也是非法调用,但在此却没有问题?!
资料:

一系列关于线程的入门文章,非常好

http://www.yoda.arachsys.com/csharp/threads/parameters.shtml

关于UI

http://msdn.microsoft.com/msdnmag/issues/03/02/Multithreading/

关于Timer

http://msdn.microsoft.com/msdnmag/issues/04/02/TimersinNET/default.aspx


posted on 2007-06-14 10:35  Dragon-China  阅读(2313)  评论(0编辑  收藏  举报