基础才是紧要,委托的二种调用示例

By admin in 4858.com on 2019年5月3日

归来目录

4858.com , 

背景:在WinForm
UI中,有时要求对控件进行比较频仍的基础代谢,如进程条、PictureBox展现摄像等。要是在主线程进行这么些刷新操作,操作还未到位就将施行下三回刷新,程序将产生错误;假使只是创造另3个线程试行这个操作,将和主线程发生竞争,产生分界面锁死(故此Windows GUI编制程序有多少个条条框框,正是只可以通过创立控件的线程来操作控件的数据,不然就恐怕产生不可预料的结果)。那时候,我们就足以用委托与异步来减轻那些主题素材。

To invoke and to begin invoke, that is a question.

Invoke和BeginInvoke都以调用委托实体的法门,前者是联合调用,即它运营在主线程上,当Invode管理时间长时,会面世堵塞的景色,而BeginInvod是异步操作,它会从新开启一个线程,所以不会租塞主线程,在利用BeginInvoke时,如若期待等待实行的结果
,能够采用EndInvoke来达成,那在.net
framework四.5后头,被打包成了async+await来兑现,代码更简洁,更易于通晓。

本文将主要透过联合调用、异步调用、异步回调多少个示范来讲课在用委托实践同叁个加法类的时候的的不一样和利弊

异步:Windows窗体控件,唯一能够从创设它的线程之外的线程中调用的是Invoke()、BeginInvoke()、EndInvoke()方法和
InvokeRequired
属性。那一个方法会切换来制造控件的线程上,以调用赋予一个委托参数的措施,该信托参数能够传递给那么些方法。

在拾二线程的应用程序中平时会波及到操作System.Windows.Forms.Control,咱们经常会境遇一些常见的难题比如“为何UI分界面被挂起了,失去响应了”,等等。其实Control类已经提供了一套轻易的体制来援救大家管理这个难点,本文将会爱慕解说该机制,对于部分广阔难点开始展览疏解。

        delegate void test();
        static void Main(string[] args)
        {
            test ts = new test(TestDelegate);
            ts.Invoke(); //不会产生新线程
            Console.WriteLine("hello");
        }

        internal static void TestDelegate()
        {
            Thread.Sleep(1000);
        }

先是,通过代码定义3个寄托和下部多个示范将在调用的点子:

关于Invoke()与BeginInvoke():当中BeginInvoke()、EndInvoke()方法是Invoke()方法的异步版本。

第1内容:

  • 基础概念。
  • 调用Invoke和BeginInvoke时,钦定的Delegate会在卓殊线程运转?如何支配使用Invoke依然BeginInvoke?
  • BeginInvoke和EndInvoke必须成对使用啊?
  • 贯彻细节。

 

那时候,在主线程中冒出了1秒的租塞,因为Invoke是①块的。

public delegate int AddHandler(int a,int b);
public class 加法类
{
   public static int Add(int a, int b)
   {
       Console.WriteLine("开始计算:" + a + "+" + b);
       Thread.Sleep(3000); //模拟该方法运行三秒
       Console.WriteLine("计算完成!");
       return a + b;
   }
}

同样点:都急需三个寄托对象作为参数。

基本功概念

.Net framework
定义了3个接口ISynchronizeInvoke来管理形式的同台及异步调用。

 1 public interface ISynchronizeInvoke
 2 {
 3     // Methods
 4     IAsyncResult BeginInvoke(Delegate method, object[] args);
 5     object EndInvoke(IAsyncResult result);
 6     object Invoke(Delegate method, object[] args);
 7 
 8     // Properties
 9     bool InvokeRequired { get; }
10 }

InvokeRequired:对于贯彻该接口的项目而言,当客户端尝试操作该类型的时候InvokeRequired将会须要客户端选择Invoke或然BeginInvoke方法来进行操作。
而Invoke和BeginInvoke则分级以协同和异步的措施来拓展操作。

基础才是紧要,委托的二种调用示例。System.Windows.Forms.Control就落成了这一个接口。

下边再来看一下BeginInvoke的得以落成

 

区别点:Invoke()是共同方法,Invoke封送的措施被实行实现前,Invoke()不会回来,从而调用者线程将被封堵;须要等待UI操作施行达成后持续实施线程时想念选用。而BeginInvoke()相反,是异步方法,方法调后立即重返,不用等待委托方法的举办完成,主线程就不会阻塞。

调用Invoke和BeginInvoke时,钦点的Delegate会在老大线程运维?怎么着决定采纳Invoke依旧BeginInvoke?

在调用Invoke和BeginInvoke的时候,客户端必须提供1个钦点的Delegate,那么这些Delegate是在丰硕线程推行那?
MSDN的定义:
       –   Invoke, Executes the specified delegate on the thread
that owns the control’s underlying window handle.
       –   BeginInvoke,Executes the specified delegate
asynchronously on the thread that the control’s underlying handle was
created on.

从概念上大家得以见到Invoke和BeginInvoke都会在具有控件Handle的线程上实施,也正是创造该控件的UI线程。

看个例证:
营造一个新的Windows Form, 增添多个Button控件到Form上,加多如下代码:

 1 public partial class Form1 : Form
 2 {
 3         private delegate void OutputMessage(string content);
 4         private IAsyncResult result = null;
 5         private System.Threading.Thread thread;
 6 
 7         public Form1()
 8         {
 9             InitializeComponent();
10         }
11 
12         private void button1_Click(object sender, EventArgs e)
13         {
14             Console.WriteLine(string.Format(“UI
Thread Id : {0}”, System.Threading.Thread.CurrentThread.ManagedThreadId));
15 
16             thread = new System.Threading.Thread(new System.Threading.ThreadStart(this.Test));
17             thread.Start();
18         }
19 
20         private void Test()
21         {
22             Console.WriteLine(string.Format(“New
Thread Id : {0}”, System.Threading.Thread.CurrentThread.ManagedThreadId));
23             Console.WriteLine(“Start executing…”);
24             this.Invoke(new OutputMessage(this.WriteMessage), “Hello
World!”);
25             //result = this.BeginInvoke(new OutputMessage(this.WriteMessage), “hello wrold!”);
26 
27             Console.WriteLine(“Finished executing…”);
28         }
29 
30         private void WriteMessage(string content)
31         {
32             Console.WriteLine(string.Format(“Invoke 
Thread Id : {0}”, System.Threading.Thread.CurrentThread.ManagedThreadId));
33             Console.WriteLine(content);
34         }
35 }

点击Button控件,Console输出如下:

UI Thread Id : 9
New Thread Id : 10
Start executing…
Invoke Thread Id : 9
Hello World!
Finished executing…

从上面包车型大巴输出可以见到(大青部分在主线程,月光蓝部分在新开启线程),就算调用者是在一个新开发银行的线程中,可是Invoke中所内定Delegate是在主UI线程中实施的,而且是一齐的,也正是说:唯有“Hello
World!”被输出后,新运维线程才会继续推行。

将第3四行代码注释掉,同时开发第壹5行代码,举办BeginInvoke调用,输出如下:

UI Thread Id : 9
New Thread Id : 10
Start executing…
Finished executing…
Invoke Thread Id : 9
Hello World!

 

从地方的输出能够看到(雪青部分在主线程,石黄部分在新开启线程),在进行BeginInvoke调用的时候,钦命的Delegate照旧在主UI线程中运转。但是BeginInvoke并不会卡住新开启的调用者线程,调用者线程将BeginInvoke交给施行线程后,会继续试行。所以在下面的例子中“Finished
executing…”会早早“Hello World!”输出。

以上正是Invoke与BeginInvoke的同样点与区别点,3个是一起实践,三个是异步奉行,二个会杜绝调用者线程,1个不会。但都以在UI主线程(创制该控件的线程)上海展览中心开。

        delegate string test();
        static void Main(string[] args)
        {
            test ts = new test(TestDelegate);
           IAsyncResult result = ts.BeginInvoke(null,null); //会在新线程中执行
            string resultstr=ts.EndInvoke(result);
            Console.WriteLine(resultstr);
        }

        internal static string TestDelegate()
        {
            Thread.Sleep(1000);
            return "hello"
        }

共同调用

Invoke方法的首要作用就是赞助你在UI线程上调用委托所内定的点子。Invoke方法首先检查发出调用的线程(即眼下线程)是还是不是UI线程,倘若是,直接实行委托指向的章程;如果不是,它将切换来UI线程,然后实施委托指向的措施。不管当前线程是还是不是UI线程,Invoke都打断直到委托指向的艺术实施落成,然后切换回发出调用的线程(要是要求的话),再次来到。

BeginInvoke和EndInvoke必须成对使用呢?

不必要,
Control的BeginInvoke方法不必要在每一回调用后,再调用相应的EndInvoke方法。EndInvoke方法的来意是获得异步实行办法的重返值,借使要求利用重回值,恐怕被ref、out标志的参数,则须求被调用。

再看一下地点的例证,改换Test以及WriteMessage方法:

 1         private void Test()
 2         {
 3             Console.WriteLine(string.Format(“New Thread Id : {0}”, System.Threading.Thread.CurrentThread.ManagedThreadId));
 4             Console.WriteLine(“Start executing…”);
 5 
 6             result = this.BeginInvoke(new OutputMessage(this.WriteMessage), “Hello World!”);
 7             Console.WriteLine(“Finished executing…”);
 8 
 9             Console.WriteLine(“Start end invoke…”);
10             object retValue = this.EndInvoke(this.result);
11             Console.WriteLine(“Finished end invoke…”);
12         }
13 
14         private void WriteMessage(string content)
15         {
16             System.Threading.Thread.Sleep(5000);
17             Console.WriteLine(string.Format(“Invoke Thread Id : {0}”, System.Threading.Thread.CurrentThread.ManagedThreadId));
18             Console.WriteLine(content);
19         }

出口如下:

UI Thread Id : 9
New Thread Id : 10
Start executing…
Finished executing…
Start end invoke…
Invoke Thread Id : 9
Hello World!
Finished end invoke…

 

在WriteMessage中,大家让当前线程Sleep 伍秒。

输出大青色的部分是在主UI线程上实践的,天青的有个别是在新开启的线程上推行。能够看到调用EndInvoke之后,调用者线程会被堵塞,直至BeginInvoke所钦点的主意实行完成,也正是说倘若EndInvoke是共同实行的,会挑起线程堵塞。

Control的BeginInvoke\EndInvoke与Delegate的BeginInvoke\EndInvoke是不一致样的,Delegate供给BeginInvoke与EndInvoke成对调用,而Control没有需求那样做,从接口也得以看出来,Control的BeginInvoke无需传入回调函数,而Delegate则是必须的。

得以完毕细节

 

那就是说Control是哪些落成Invoke和BeginInvoke的哪?

第三大家要驾驭Windows窗口编制程序的1个平整,倘诺须求操作控件数据以来,只好通过创造该控件的线程来操作。那样做的目标是为着制止线程竞争,产生不可预见的错误。那也正是为啥在上边包车型地铁事例中Invoke和BeginInvoke所钦点的Delegate都会在主UI线程中试行的由来。

 

这就是说调用者线程如何告诉UI线程来实行相应动作哪?当然是透过SendMessage只怕PostMessage向UI线程发送音信来进行了。

因此反编写翻译Control的代码大家得以看看Invoke和BeginInvoke都以通过PostMessage来完结的,只可是Invoke调用的时候,调用者线程会不停等待直至Invoke甘休。见下边包车型客车代码片段:

 1 private object MarshaledInvoke(Control caller, Delegate method, object[] args, bool synchronous)
 2 {
 3    //…向UI线程发送音讯,该消息会在Control的WndProc中拍卖。
 4     UnsafeNativeMethods.PostMessage(new HandleRef(this, this.Handle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);
 5     
 6    //…
 七    // 要是是同步操作,则等待,不然在此以前已经回来。
 8     if (!entry.IsCompleted)
 9     {
10         this.WaitForWaitHandle(entry.AsyncWaitHandle);
11     }
12    //…
13 }

 

全文完。

 

上边包车型大巴代码会在新线程中施行,并且平会对主线程发生租塞,同时它可以收获本人的重临值,使用EndInvoke完成!

信托的Invoke方法用来开始展览同步调用。同步调用也足以叫阻塞调用,它将卡住当前线程,然后实行调用,调用实现后再持续向下开展。代码如下

BeginInvoke : 早先2个异步的请求,调用线程池(ThreadPool
)中一个线程来进行,方法调后立刻重返,不用等待委托方法的实行完成,主线程就不会卡住。Control.BeginInvoke方法并不会另开线程。

谢谢阅读!小小的知识点我们也要完美了然。

public class 同步调用
{
        static void Main()
        {
            Console.WriteLine("===== 同步调用 SyncInvokeTest =====");
            AddHandler handler = new AddHandler(加法类.Add);
            int result = handler.Invoke(1, 2);
            Console.WriteLine("继续做别的事情。。。");
            Console.WriteLine(result);
            Console.ReadKey();
        }      
}

再次回到 IAsyncResult 对象(异步的骨干):IAsyncResult
简单来讲,他是储存异步操作景况音讯的2个接口,也得以用她来收尾方今异步。

重临目录

一同调用会阻塞线程,假若是要调用一项艰辛的干活(如大批量IO操作),只怕会让程序停顿十分短日子,产生糟糕的用户体验,那时候异步调用就很有至关重要了。

瞩目:
BeginInvoke和EndInvoke必须成对调用。固然不供给重临值,但EndInvoke依然必须调用,不然或然会变成内部存款和储蓄器泄漏。

异步调用

IAsyncResult.AsyncState
属性:获取用户定义的对象,它界定或包蕴关于异步操作的音信。

异步调用不打断线程,而是把调用塞到线程池中,程序主线程或UI线程能够继续实践。委托的异步调用通过BeginInvoke和EndInvoke来促成。代码如下:

率先,通过代码定义二个信托和底下多少个示范将在调用的办法:

public class 异步调用
{
        static void Main()
        {
            Console.WriteLine("===== 异步调用 AsyncInvokeTest =====");
            AddHandler handler = new AddHandler(加法类.Add);
            //IAsyncResult: 异步操作接口(interface)
            //BeginInvoke: 委托(delegate)的一个异步方法的开始
            IAsyncResult result = handler.BeginInvoke(1, 2, null, null);
            Console.WriteLine("继续做别的事情。。。");
            //异步操作返回
            Console.WriteLine(handler.EndInvoke(result));
            Console.ReadKey();
        }
}

        //委托

能够看来,主线程并未等待,而是径直向下运营了。不过难点依旧留存,当主线程运转到EndInvoke时,假诺此时调用未有终止(那种景色很恐怕出现),那时为了等待调用结果,线程依旧会被堵塞。

        public delegate int AddDel( int a, int b);

异步委托,也足以参考如下写法:

        //加法

Action<object> action=(obj)=>method(obj);

        static int Add( int a, int b)

action.BeginInvoke(obj,ar=>action.EndInvoke(ar),null);

        {

简轻易单两句话就能够产生一部操作。

            Console .WriteLine(“初叶估量:” + a + “+” + b);

异步回调

            Thread .Sleep(三千);//模拟该措施运转三秒

用回调函数,当调用截至时会自动调用回调函数,化解了为等候调用结果,而让线程照旧被堵塞的局面。代码如下:

            Console .WriteLine(“总结实现!” );

public class 异步回调
{
        static void Main()
        {
            Console.WriteLine("===== 异步回调 AsyncInvokeTest =====");
            AddHandler handler = new AddHandler(加法类.Add);
            //异步操作接口(注意BeginInvoke方法的不同!)
            IAsyncResult result = handler.BeginInvoke(1,2,new AsyncCallback(回调函数),"AsycState:OK");
            Console.WriteLine("继续做别的事情。。。");
            Console.ReadKey();
        }

        static void 回调函数(IAsyncResult result)
        {     
             //result 是“加法类.Add()方法”的返回值
            //AsyncResult 是IAsyncResult接口的一个实现类,空间:System.Runtime.Remoting.Messaging
            //AsyncDelegate 属性可以强制转换为用户定义的委托的实际类。
            AddHandler handler = (AddHandler)((AsyncResult)result).AsyncDelegate;
            Console.WriteLine(handler.EndInvoke(result));
            Console.WriteLine(result.AsyncState);
        }
}

            return a + b;

自家定义的寄托的品类为AddHandler,则为了访问
AddHandler.EndInvoke,必须将异步委托强制调换为
AddHandler。能够在异步回调函数(类型为 AsyncCallback)中调用
MAddHandler.EndInvoke,以赢得最初提交的 AddHandler.BeginInvoke 的结果。

        }

注意:

手拉手调用

(1)int result = handler.Invoke(1,2);

寄托的Invoke方法用来拓展协同调用。同步调用也能够叫阻塞调用,它将封堵当前线程,然后实践调用,调用完成后再持续向下打开。

为什么Invoke的参数和再次回到值和AddHandler委托是同壹的啊?

1块调用会阻塞线程,如若是要调用壹项艰难的做事(如大批量IO操作),也许会让程序停顿不长日子,产生不好的用户体验,那时候异步调用就很有供给了。

答:Invoke方法的参数很简单,三个委托,二个参数表(可选),而Invoke方法的珍视功效便是扶持你在UI线程上调用委托所钦点的章程。Invoke方法首先检查发出调用的线程(即目前线程)是还是不是UI线程,若是是,直接实行委托指向的艺术,即便不是,它将切换成UI线程,然后实践委托指向的不贰秘诀。不管当前线程是否UI线程,Invoke都短路直到委托指向的方法实施实现,然后切换回发出调用的线程(借使需求的话),重临。

        static void Main(string [] args)

从而Invoke方法的参数和重临值和调用他的委托应该是千篇一律的。

        {

(2)IAsyncResult result = handler.BeginInvoke(1,2,null,null);

            Console .WriteLine(“===== 同步调用 SyncInvokeTest =====” );

BeginInvoke : 初阶二个异步的伏乞,调用线程池中二个线程来实践,

            AddDel del = new AddDel(Add);

归来IAsyncResult 对象(异步的中坚). IAsyncResult
简单的说,他存款和储蓄异步操作的情形音讯的多个接口,也可以用她来收尾近日异步。

            int result = del.Invoke(1, 2);

在意:
BeginInvoke和EndInvoke必须成对调用.尽管无需再次回到值,但EndInvoke照旧必须调用,不然或然会导致内部存储器泄漏。

            Console .WriteLine(“继续做其余事情。。。” );

(3)IAsyncResult.AsyncState 属性:

            Console .WriteLine(result);

获取用户定义的目的,它界定或含有关于异步操作的新闻。

            Console .ReadKey();

转载:

        }

异步调用

异步调用不打断线程,而是把调用塞到线程池中,程序主线程或UI线程能够继续实践。委托的异步调用通过BeginInvoke和EndInvoke来促成。

能够观察,主线程并未等待,而是一贯向下运维了。可是难点照旧存在,当主线程运营到EndInvoke时,假使那时候调用未有完结(那种情状很或者现身),那时为了等待调用结果,线程仍旧会被卡住。

        static void Main()

        {

            Console .WriteLine(“===== 异步调用 AsyncInvokeTest =====” );

            AddDel del = new AddDel(Add);

            //IAsyncResult: 异步操作接口(interface)

            //BeginInvoke: 委托(delegate)的三个异步方法的起来

            IAsyncResult result = del.BeginInvoke(1, 2, null , null);

            Console .WriteLine(“继续做其余事情……” );

            //异步操作重回

            Console .WriteLine(del.EndInvoke(result));

            Console .ReadKey();

        }

异步回调

用回调函数,当调用截至时会自动调用回调函数,消除了为等候调用结果,而让线程仍旧被封堵的规模。

        static void Main()

        {

            Console .WriteLine(“===== 异步回调 AsyncInvokeTest =====” );

            AddDel del = new AddDel(Add);

            //异步操作接口(注意BeginInvoke方法的不等!)

            del.BeginInvoke(1, 2, new AsyncCallback (AddAsync),
“AsyncState:OK”);

            Console .WriteLine(“继续做别的事情。。。” );

            Console .ReadKey();

        }

        ///

        /// 回调函数

        ///

        static void AddAsync(IAsyncResult ar)

        {

            //AsyncResult 是IAsyncResult接口的三个兑现类,空间:       
                System.Runtime.Remoting.Messaging

            //AsyncDelegate 属性能够强制转变为用户定义的嘱托的实际类。

            AddDel del = ((AsyncResult )ar).AsyncDelegate as AddDel;

            Console .WriteLine(del.EndInvoke(ar));

            Console .WriteLine(ar.AsyncState);

        }

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有