从UI体验下异步调用的四种方法
我下面的DEMO是从MSDN下摘抄过来的,可是MSDN是从控制台打印出来,
如果要比较直观地理解线程间的操作,从直观体验上觉得还是直接从UI方面感觉比较好
而下面主要是介绍那四处方法的使用,为什么用,怎么用,
因为除了第一种异步回调之外,其他的界面都卡在那里一动不动,但并不是说其他三种不好,只是他们还有别的地方要用而已
而里面红色的注释,我想已经能表达我想说的,而代码我还不知怎么像有的朋友那样贴上去好看点,可能比较难看,将就点喽
//委托,其就是函数签名,从这里可以看出,参数与返回值是与函数一模一样的,
//而委托主要也是用于回调同步异步,观察者模式,改天再好好回味一下观察者模式
public delegate string AsyncMethodCaller(int callDuration, out int threadId);
//这里写上返回值只是为了好玩,经常写没返回值的委托,写写看,嘿嘿
public delegate string InvokeMethodCaller(string s);
class AsyncDemo
{
// The method to be executed asynchronously.
public string TestMethod(int callDuration, out int threadId)
{
Console.WriteLine("Test method begins.");
for (int i = 0; i < callDuration / 1000; i++)
{
Console.WriteLine("次线程:" + callDuration);
Thread.Sleep(1000);
}
threadId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine(threadId.ToString());
return String.Format("My call time was {0}.", callDuration.ToString());
}
}
private static int threadId;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//变量
string rethrnValue = string.Empty;
AsyncDemo ad = new AsyncDemo();
//把AD对象的TestMethod方法注册给了AsyncMethodCaller委托,实例名CallbackEvent,
//再由这个CallbackEvent去异步调用,BeginInvoke ,EndInvoke,Invoke等三个同异步方法
//而为什么有this.invoke这个嘛!就不知跟这里所说的一样不,希望有朋友给我指出下,谢谢
AsyncMethodCaller CallbackEvent = new AsyncMethodCaller(ad.TestMethod);
//方法1,指定一个回调函数,可惜在回调函数里还是必须得用INVOKE,证明,
//得到的数据还是在别的线程而已,而CONSOLE,MESSAGEBOX能用只能说是静态的
//如果启动异步调用的线程不需要是处理结果的线程,则可以在调用完成时执行回调方法。回调方法在 ThreadPool 线程上执行。
//若要使用回调方法,必须将引用回调方法的 AsyncCallback 委托传递给 BeginInvoke。
//也可以传递包含回调方法将要使用的信息的对象。例如,可以传递启动调用时曾使用的委托,以便回调方法能够调用 EndInvoke。
//主要理解回调方法将要使用的信息的对象,IAsyncResult,这个,也可以理解下
//回调的方法 new AsyncCallback(Callback)
//使用的委托 CallbackEvent
//前两个是委托的参数
CallbackEvent.BeginInvoke(3000, out threadId, new AsyncCallback(Callback), CallbackEvent);
//方法2 一直轮循,一直都是UI线程在调,判断是否完成
//MSDN:您可以使用由 BeginInvoke 返回的 IAsyncResult 的 IsCompleted 属性来发现异步调用何时完成。
//从用户界面的服务线程中进行异步调用时可以执行此操作。
//轮询完成允许调用线程在异步调用在 ThreadPool 线程上执行时继续执行。
//尽管的确是界面也可以操作,但是,实际上来拖动界面是很难的,证明这时间是很短的,
//对于显示一个固定的界面不能动的进度条还可以考虑下,其他的
//愚见就算了吧,就用第一种吧,其实这下三种道理都是一样的,开启一线程在做,主线程等待,等其完成,取其返回值,
//但我们知道,在NET1.1线程间想传值是很麻烦,而二点零回调等等,则帮我们做了这么些事情吧
//IAsyncResult ar1 = CallbackEvent.BeginInvoke(3000, out threadId, null, null);
//while (!ar1.IsCompleted)
//{
// //Thread.Sleep(20);
// //richTextBox1.Text += 8 + ""n";
//}//循环直到异步完成
// rethrnValue = CallbackEvent.EndInvoke(out threadId, ar1);
//richTextBox1.Text += rethrnValue + " " + threadId;
//方法3 使用WAITONE,其实道理与轮循一样,或许只是少了个WHILE循环
//MSDN :如果您使用 WaitHandle,则在异步调用完成之前或之后,在通过调用 EndInvoke 检索结果之前,还可以执行其他处理。
//其实跟上下两种方法是一样的,感觉好拙呀!只是写法不同而已
//IAsyncResult result = CallbackEvent.BeginInvoke(3000, out threadId, null, null);
//// while (!result.IsCompleted)
////{
//// Application.DoEvents();
////}
//result.AsyncWaitHandle.WaitOne();
//rethrnValue = CallbackEvent.EndInvoke(out threadId, result);
//richTextBox1.Text += rethrnValue + " " + threadId.ToString();
//方法四 一BeginInvoke,另一线程就启动,而在与EndInvoke之间,主线程是可以干这里面代码的事情的,
//MSDN:EndInvoke 可能会阻止调用线程,因为它直到异步调用完成之后才返回。
//这种技术非常适合文件或网络操作,但是由于 EndInvoke 会阻止它,所以不要从服务于用户界面的线程中调用它。
//IAsyncResult result = CallbackEvent.BeginInvoke(3000, out threadId, null, null);
////while (!result.IsCompleted)
////{
//// Application.DoEvents();
////}
// rethrnValue = CallbackEvent.EndInvoke(out threadId, result);
//richTextBox1.Text += rethrnValue + " " + threadId.ToString();
}
private void Callback(IAsyncResult ar)
{
AsyncMethodCaller caller = (AsyncMethodCaller)ar.AsyncState;
string s = caller.EndInvoke(out threadId, ar);
//如果这样写,其实就是另开一个同步调用了,而把上面那个异步调用再做一遍,一时很傻的想法
//放在这里是想说明下同步而已
//string s = caller.Invoke(3000, out threadId);
//而如果用下面这个,则会发生线程间调用的问题,提示不是从创建线程赋值的,一般这种问题都是没有与UI线程同步
//richTextBox1.Text += threadId;
//与UI线程同步的写法
this.Invoke(new InvokeMethodCaller(setText), s);
}
private string setText(string s)
{
//到这里了,已经是跟UI是同一线程的了
richTextBox1.Text += s;
return s;
}
而里面////的地方是我特意测试用的,偶尔可能还能让你体会一下,代码很短,或许你COPY一下就可以在自已机子上运行,但跟着抄一遍,最好是理解后自已写一份出来,或许收益更大吧
改天再把读取网络大的文件流,SOCKET等等结合下写出来,希望朋友指出错误,谢谢
加油EVERYONE
如果要比较直观地理解线程间的操作,从直观体验上觉得还是直接从UI方面感觉比较好
而下面主要是介绍那四处方法的使用,为什么用,怎么用,
因为除了第一种异步回调之外,其他的界面都卡在那里一动不动,但并不是说其他三种不好,只是他们还有别的地方要用而已
而里面红色的注释,我想已经能表达我想说的,而代码我还不知怎么像有的朋友那样贴上去好看点,可能比较难看,将就点喽
//委托,其就是函数签名,从这里可以看出,参数与返回值是与函数一模一样的,
//而委托主要也是用于回调同步异步,观察者模式,改天再好好回味一下观察者模式
public delegate string AsyncMethodCaller(int callDuration, out int threadId);
//这里写上返回值只是为了好玩,经常写没返回值的委托,写写看,嘿嘿
public delegate string InvokeMethodCaller(string s);
class AsyncDemo
{
// The method to be executed asynchronously.
public string TestMethod(int callDuration, out int threadId)
{
Console.WriteLine("Test method begins.");
for (int i = 0; i < callDuration / 1000; i++)
{
Console.WriteLine("次线程:" + callDuration);
Thread.Sleep(1000);
}
threadId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine(threadId.ToString());
return String.Format("My call time was {0}.", callDuration.ToString());
}
}
private static int threadId;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//变量
string rethrnValue = string.Empty;
AsyncDemo ad = new AsyncDemo();
//把AD对象的TestMethod方法注册给了AsyncMethodCaller委托,实例名CallbackEvent,
//再由这个CallbackEvent去异步调用,BeginInvoke ,EndInvoke,Invoke等三个同异步方法
//而为什么有this.invoke这个嘛!就不知跟这里所说的一样不,希望有朋友给我指出下,谢谢
AsyncMethodCaller CallbackEvent = new AsyncMethodCaller(ad.TestMethod);
//方法1,指定一个回调函数,可惜在回调函数里还是必须得用INVOKE,证明,
//得到的数据还是在别的线程而已,而CONSOLE,MESSAGEBOX能用只能说是静态的
//如果启动异步调用的线程不需要是处理结果的线程,则可以在调用完成时执行回调方法。回调方法在 ThreadPool 线程上执行。
//若要使用回调方法,必须将引用回调方法的 AsyncCallback 委托传递给 BeginInvoke。
//也可以传递包含回调方法将要使用的信息的对象。例如,可以传递启动调用时曾使用的委托,以便回调方法能够调用 EndInvoke。
//主要理解回调方法将要使用的信息的对象,IAsyncResult,这个,也可以理解下
//回调的方法 new AsyncCallback(Callback)
//使用的委托 CallbackEvent
//前两个是委托的参数
CallbackEvent.BeginInvoke(3000, out threadId, new AsyncCallback(Callback), CallbackEvent);
//方法2 一直轮循,一直都是UI线程在调,判断是否完成
//MSDN:您可以使用由 BeginInvoke 返回的 IAsyncResult 的 IsCompleted 属性来发现异步调用何时完成。
//从用户界面的服务线程中进行异步调用时可以执行此操作。
//轮询完成允许调用线程在异步调用在 ThreadPool 线程上执行时继续执行。
//尽管的确是界面也可以操作,但是,实际上来拖动界面是很难的,证明这时间是很短的,
//对于显示一个固定的界面不能动的进度条还可以考虑下,其他的
//愚见就算了吧,就用第一种吧,其实这下三种道理都是一样的,开启一线程在做,主线程等待,等其完成,取其返回值,
//但我们知道,在NET1.1线程间想传值是很麻烦,而二点零回调等等,则帮我们做了这么些事情吧
//IAsyncResult ar1 = CallbackEvent.BeginInvoke(3000, out threadId, null, null);
//while (!ar1.IsCompleted)
//{
// //Thread.Sleep(20);
// //richTextBox1.Text += 8 + ""n";
//}//循环直到异步完成
// rethrnValue = CallbackEvent.EndInvoke(out threadId, ar1);
//richTextBox1.Text += rethrnValue + " " + threadId;
//方法3 使用WAITONE,其实道理与轮循一样,或许只是少了个WHILE循环
//MSDN :如果您使用 WaitHandle,则在异步调用完成之前或之后,在通过调用 EndInvoke 检索结果之前,还可以执行其他处理。
//其实跟上下两种方法是一样的,感觉好拙呀!只是写法不同而已
//IAsyncResult result = CallbackEvent.BeginInvoke(3000, out threadId, null, null);
//// while (!result.IsCompleted)
////{
//// Application.DoEvents();
////}
//result.AsyncWaitHandle.WaitOne();
//rethrnValue = CallbackEvent.EndInvoke(out threadId, result);
//richTextBox1.Text += rethrnValue + " " + threadId.ToString();
//方法四 一BeginInvoke,另一线程就启动,而在与EndInvoke之间,主线程是可以干这里面代码的事情的,
//MSDN:EndInvoke 可能会阻止调用线程,因为它直到异步调用完成之后才返回。
//这种技术非常适合文件或网络操作,但是由于 EndInvoke 会阻止它,所以不要从服务于用户界面的线程中调用它。
//IAsyncResult result = CallbackEvent.BeginInvoke(3000, out threadId, null, null);
////while (!result.IsCompleted)
////{
//// Application.DoEvents();
////}
// rethrnValue = CallbackEvent.EndInvoke(out threadId, result);
//richTextBox1.Text += rethrnValue + " " + threadId.ToString();
}
private void Callback(IAsyncResult ar)
{
AsyncMethodCaller caller = (AsyncMethodCaller)ar.AsyncState;
string s = caller.EndInvoke(out threadId, ar);
//如果这样写,其实就是另开一个同步调用了,而把上面那个异步调用再做一遍,一时很傻的想法
//放在这里是想说明下同步而已
//string s = caller.Invoke(3000, out threadId);
//而如果用下面这个,则会发生线程间调用的问题,提示不是从创建线程赋值的,一般这种问题都是没有与UI线程同步
//richTextBox1.Text += threadId;
//与UI线程同步的写法
this.Invoke(new InvokeMethodCaller(setText), s);
}
private string setText(string s)
{
//到这里了,已经是跟UI是同一线程的了
richTextBox1.Text += s;
return s;
}
而里面////的地方是我特意测试用的,偶尔可能还能让你体会一下,代码很短,或许你COPY一下就可以在自已机子上运行,但跟着抄一遍,最好是理解后自已写一份出来,或许收益更大吧
改天再把读取网络大的文件流,SOCKET等等结合下写出来,希望朋友指出错误,谢谢
加油EVERYONE