o任飘零o

别人给了你一滴水,你给了别人些什么?
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

实现等待窗体的几种方式

Posted on 2013-02-04 13:19  o任飘零o  阅读(171)  评论(0)    收藏  举报

实现等待窗体的几种方式

===========================================================

作者: dotnet(http://dotnet.itpub.net) 发表于: 2007.06.13 18:35 分类: .NET技术开发  出处: http://dotnet.itpub.net/post/20969/294521  ---------------------------------------------------------------

 

实现等待窗体的几种方式:

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

准备资料

安全访问控件成员

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

如: 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

实现等待窗体的几种方式:

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