invoke 订阅
《INVOKE》 [1]  为《机动战士高达seed》片头曲(1-13集),由日本滋贺县野洲市出身的歌手、演员、声优西川贵教(t.m.revolution即西川贵教)演唱。 展开全文
《INVOKE》 [1]  为《机动战士高达seed》片头曲(1-13集),由日本滋贺县野洲市出身的歌手、演员、声优西川贵教(t.m.revolution即西川贵教)演唱。
信息
编    曲
浅倉大介
歌曲时长
4:18
歌曲原唱
T.M.Revolution
发行时间
2002-10-30
谱    曲
浅倉大介
音乐风格
摇滚
外文名称
INVOKE
所属专辑
INVOKE
歌曲语言
日语
填    词
井上秋緒
invoke单曲信息
【INVOKE——机动战士高达seed OP1】 【所属专辑】《机动战士高达 IV》【作词】井上秋绪【作曲】浅仓大介【歌】T.M. Revolution
收起全文
精华内容
下载资源
问答
  • 2021-03-16 16:59:25

    Java 动态代理,invoke() 自动调用原理,invoke() 参数

    本文介绍了静态代理和动态代理的概念,并且用代码、注释、图示和源码的方式来分析动态代理原理和invoke()自动调用原理。


    学习动态代理,先从静态代理入手

    静态代理

    假如现在我们面临一个需求,把一个项目中所有访问数据库的方法都记录日志。最直接的方法是修改所有代码,在每个方法里面都加入写日志代码:

    public class Dao {
    	User findUserbyId(int id) {
    		// 业务代码
    		xxx;
    		// 加入写日志代码
    		xxx;
    	}
    	
    }
    

    但是这样工作量会很大,并且模块之间是耦合的,比如下次的需求是修改记录日志的内容,那么又要去修改所有业务代码,显然这种方法是不可取的。

    如果不可以修改业务代码呢?

    很容易就想到静态代理,写代理类对业务代码进行调用,我们用老师教学生举例子:

    /**
    * 老师接口,老师可以教学生
    */
    public interface Teacher { 
        void teach();
    }
    
    /**
    * 老师接口的实现类,具体实现 teach() 方法
    */
    public class RealTeacher implements Teacher {
        @Override
        public void teach() {
            System.out.println("I am a teacher, I am teaching!");
        }
    }
    
    /**
    * 老师代理对象,增强 teach() 方法
    */
    public class TeacherProxy implements Teacher {
        Teacher teacher = new RealTeacher();
        @Override
        public void teach() {
        	// 这里 dosomething() 可以实现一些代理类的功能
        	dosomething();
        	// 这里执行 RealTeacher 的 teach() 方法
        	teacher.teach();
            dosomething();
        }
    }
    
    /**
    * 测试代理类
    */
    public class ProxyTest {
        public static void main(String args[]) {
        	Teacher teacher = new TeacherProxy();
        	// 这里执行代理类的 teach() 方法
        	teacher.teach();
        }
    }
    

    用图示表示上面静态代理的代码,可以描述为:

    从图片和代码可以看出,这里只是用一个新对象去包含原来的 RealTeacher 对象,看似多此一举,但是确实一个不改变原来的代码,对方法进行增强的好办法。但是,如果我们要增强很多方法,比如文章开头的例子,需要对所有访问数据库的方法添加日志记录,如果用静态代理,要为每一个对象编写代理累,代码量十分庞大,也难以维护。

    动态代理

    既然为每一个需要代理的对象(下文通称为目标对象)都编写相应的代理类很麻烦,那么能不能不写死代理对象和目标对象,只写一个代理对象就可以使用不同的目标对象呢?
    答案是可以的,就是用动态代理。动态代理的代理对象不依赖于目标对象,不和目标对象耦合。其原理是把目标对象的类型信息(比如接口)作为参数,利用 Java 支持的反射来创建代理对象,这样,就解除了代理对象和目标对象的耦合,一个代理对象可以适用一些列的目标对象。用图表示为:
    在这里插入图片描述
    如上图所示,目标对象作为一个参数,动态代理得到这个参数之后分成三步走:

    1. 获得目标对象的接口信息和类加载器,然后根据这两个信息来反射得到代理类的 Class
    2. 获得目标对象的 Class
    3. 根据1 2两步获得的接口,代理类 Class,目标类 Class 实例化一个代理对象

    这样, 就创建了一个代理对象。从图中可以看出,动态代理并没有和目标对象绑定,而是把目标对象作为一个参数。

    JDK 提供了 java.lang.reflect.InvocationHandler 接口和 java.lang.reflect.Proxy 类来实现上述反射等功能,其中 java.lang.reflect.InvovationHandler 接口用于绑定目标对象需要增强的方法,java.lang.reflect.Proxy 提供静态方法 NewProxyInstance() 用于创建一个代理类实例,这样,这个代理类实习就被创建出来并且通过嵌入 InvocationHandler 绑定了目标类的方法。
    上面的静态代理代码的例子改成动态代理:

    /**
     * 老师接口,老师可以教学生
     */
    public interface Teacher {
        void teach();
    }
    
    
    /**
     * 老师接口的实现类,具体实现 teach() 方法
     */
    public class RealTeacher implements Teacher {
        @Override
        public void teach() {
            // 老师正在教书
            System.out.println("I am a teacher, I am teaching!");
        }
    }
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    /**
     * 代理类,根据接口等信息动态地创建代理对象,其中 MyProxy 是 InvocationHandler 的实现类,用于绑定方法
     */
    public class MyProxy implements InvocationHandler {
        // tar用于接收目标类的参数
        private Object tar;
    
        // 绑定目标类,根据目标类的类加载器和接口创建代理对象,并返回
        public Object bind(Object target) {
            this.tar = target;
            // 注意:此处代码返回代理类
            return Proxy.newProxyInstance(tar.getClass().getClassLoader(), 
            tar.getClass().getInterfaces(), this);
        }
        // invoke() 方法用于方法的增强
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            // 执行一些方法
            System.out.println("Do something before");
    
            // 目标类的方法执行,这里实际上是执行目标对象的方法,
            // 也就是 bind(Object target)参数 object target 的方法
            result = method.invoke(tar, args);
    
            System.out.println("Do something after");
            return result;
        }
    }
    
    
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyTest {
        public static void main(String[] args) throws Throwable {
            MyProxy proxy = new MyProxy();
    
            // 传入目标对象,进行绑定
            Teacher teacher = (Teacher)proxy.bind(new RealTeacher());
    
            // 执行代理对象的方法
            teacher.teach();
        }
    }
    

    测试函数的输出应该为:

    Do something before
    I am a teacher, I am teaching!
    Do something after

    改写成内部类的方式可以更清楚的看到:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyTest {
        public static void main(String[] args) throws Throwable {
            // 这里能清楚地看到,目标对象 RealTeacher 是一个纯粹的参数
            Teacher teacher = (Teacher)getProxy(new RealTeacher());
            // teacher 是接口 Teacher 的一个实现类,
            // 完全从多态的角度也能想到执行的实际上是 RealTeacher 的 teach() 方法
            teacher.teach();
        }
        public static Object getProxy(Object target) throws Throwable{
    
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Object result = null;
                            System.out.println("Do something before");
                            result = method.invoke(target, args);
                            System.out.println("Do something after");
                            return result;
                        }
            });
        }
    }
    
    

    此源代码在 GitHub 上。
    目标对象 RealTeacher 可以看成是一个纯粹的参数,不和代理类耦合。
    在应用的时候,可以把具体实现过程忽略,把

    Teacher teacher = (Teacher)getProxy(new RealTeacher());

    看成一个黑箱,它输入一个目标类作为参数,返回一个实现了 Teacher 接口的代理类。到此为止,我们成功使用了动态代理,在没有和目标类 RealTeacher 绑定的情况下,创建了一个代理类,增强了 teach() 方法。

    进一步深究代理类 teacher,invoke()

    为了进一步探究生成的代理类 teacher 到底是什么一个情况,我们输出 teach 的一些类型信息:

        public static void main(String[] args) throws Throwable {
            // 这里能清楚地看到,目标对象 RealTeacher 是一个纯粹的参数
            Teacher teacher = (Teacher)getProxy(new RealTeacher());
            // teacher 是接口 Teacher 的一个实现类,
            // 完全从多态的角度也能想到执行的实际上是 RealTeacher 的 teach() 方法
            teacher.teach();
    
            System.out.println(teacher.getClass().getSuperclass());//输出是class java.lang.reflect.Proxy
    
            Class<?>[] interfaces = teacher.getClass().getInterfaces();
    
            for(Class<?> i : interfaces){
                System.out.println(i);// 输出是interface Teacher
            }
    
            Method[] methods = teacher.getClass().getDeclaredMethods();
    
            for (Method method : methods) {
                System.out.println(method);
                // 输出是:
                // public final boolean com.sun.proxy.$Proxy0.equals(java.lang.Object)
                // public final java.lang.String com.sun.proxy.$Proxy0.toString()
                // public final int com.sun.proxy.$Proxy0.hashCode()
                // public final void com.sun.proxy.$Proxy0.teach() 
    
            }
        }
    

    由上述输出可以发现,

    Teacher teacher = (Teacher)getProxy(new RealTeacher());

    创建的代理类 teacher 实际是名字是 $Proxy0,它父类 Proxy,实现了 Teacher 接口,实现了 teach() 方法,所以代码

    teacher.teach();
    

    理所当然可以执行。

    但是,我们知道代码 teacher.teach(); 最终执行了invoke()方法,那么这个teach()方法到底怎么和invoke()有关联的呢?

    我们打开 teach 的方法 Proxy.newProxyInstance()源码的注释,关键的是这几行:

     * Returns a proxy instance for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
    
    @param   loader the class loader to define the proxy class
         * @param   interfaces the list of interfaces for the proxy class
         *          to implement
         * @param   h the invocation handler to dispatch method invocations to
         * @return  a proxy instance with the specified invocation handler of a
         *          proxy class that is defined by the specified class loader
         *          and that implements the specified interfaces
    

    再看 Proxy 类的注释:

    * {@code Proxy} provides static methods for creating objects that act like instances
     * of interfaces but allow for customized method invocation.
     * To create a proxy instance for some interface {@code Foo}:
     * <pre>{@code
     *     InvocationHandler handler = new MyInvocationHandler(...);
     *     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
     *                                          new Class<?>[] { Foo.class },
     *                                          handler);
     * }</pre>
     *
     * <p>
     * A <em>proxy class</em> is a class created at runtime that implements a specified
     * list of interfaces, known as <em>proxy interfaces</em>. A <em>proxy instance</em>
     * is an instance of a proxy class.
     *
     * Each proxy instance has an associated <i>invocation handler</i>
     * object, which implements the interface {@link InvocationHandler}.
     * A method invocation on a proxy instance through one of its proxy
     * interfaces will be dispatched to the {@link InvocationHandler#invoke
     * invoke} method of the instance's invocation handler, passing the proxy
     * instance, a {@code java.lang.reflect.Method} object identifying
     * the method that was invoked, and an array of type {@code Object}
     * containing the arguments.  The invocation handler processes the
     * encoded method invocation as appropriate and the result that it
     * returns will be returned as the result of the method invocation on
     * the proxy instance.
     *
    

    由注释发现,Proxy.newProxyInstance()方法返回一个代理对象的实例,这个实例是和一个特殊的 InvocationHandler 相关的,再回看我们写的代码:
    在这里插入图片描述
    我们实例化的匿名内部类 InvocationHandler 正是有 invoke() 方法,所以刚才的问题:

    那么这个teach()方法到底怎么和invoke()有关联的呢?

    是因为代理对象实例 teacher 的父类是 Proxy,由 Proxy 去和我们实例化的匿名内部类 InvocationHandler 去关联

    再看实例对象 teacher(也就是 $Proxy0 )的反编译代码:(此代码来自这里

    public final class $Proxy0 extends Proxy implements Subject {  
        private static Method m1;  
        private static Method m0;  
        private static Method m3;  
        private static Method m2;  
      
        static {  
            try {  
                m1 = Class.forName("java.lang.Object").getMethod("equals",  
                        new Class[] { Class.forName("java.lang.Object") });  
      
                m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
                        new Class[0]);  
      
                m3 = Class.forName("***.RealSubject").getMethod("request",  
                        new Class[0]);  
      
                m2 = Class.forName("java.lang.Object").getMethod("toString",  
                        new Class[0]);  
      
            } catch (NoSuchMethodException nosuchmethodexception) {  
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
            } catch (ClassNotFoundException classnotfoundexception) {  
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
            }  
        } //static  
      
        public $Proxy0(InvocationHandler invocationhandler) {  
            super(invocationhandler);  
        }  
      
        @Override  
        public final boolean equals(Object obj) {  
            try {  
                return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final int hashCode() {  
            try {  
                return ((Integer) super.h.invoke(this, m0, null)).intValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        public final void teach() {  
            try {  
                super.h.invoke(this, m3, null);  
                return;  
            } catch (Error e) {  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final String toString() {  
            try {  
                return (String) super.h.invoke(this, m2, null);  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
    }  
    

    也验证了当执行 teacher.teach() 的时候,也就是执行 $Proxy0.teach() 的时候,会执行 super.h.invoke(),也就是执行父类 Proxy 的 invoke(),而父类 Proxy 是和 InvocationHandler 是关联的,总结为:

    teacher.teach() --> $Proxy0.teach() --> super.h.invoke() --> proxy.invoke() --> invocationHandler.invoke()

    图示可以表示为:
    在这里插入图片描述

    invoke() 的参数

    从代码可以看出,invoke() 方法有三个参数:

    • Object proxy
    • Method method
    • Object[] args

    我们在 invoke() 中输出 proxy 的名字:

    return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        	// 输出proxy名字
                            System.out.println(proxy.getClass().getName());
                            
                            Object result = null;
                            System.out.println("Do something before1");
                            result = method.invoke(target, args);
                            System.out.println("Do something after1");
                            return result;
                        }
            });
    

    输出是:
    在这里插入图片描述
    上文说代理类名字是 $Proxy0,所以实际上 proxy 参数就是代指反射创建的代理类 $Proxy0,之所以不用 this,因为 this 代表的是 InvocationHandler() 这个匿名内部类的实例。

    同样的,我们看 $Proxy0 的反编译代码:
    在这里插入图片描述
    这个代码中的this就是 $Proxy0 对象本身,也是作为 invoke() 的第一个参数,也就是 proxy 进行参数传递。所以也能得到 proxy 参数就是代指反射创建的代理类 $Proxy0 的结论。

    对于第二个参数 method,我们看 Method 的注释:

     * A {@code Method} provides information about, and access to, a single method
     * on a class or interface.  The reflected method may be a class method
     * or an instance method (including an abstract method).
    

    可以看出第二个参数 method 用于绑定的目标类的方法

    第三个参数是方法的参数

    总结

    代理模式是一种不改变源代码对方法进行增强的好方法,但是静态代理代理类和目标类是绑定耦合的。

    动态代理的特点是:

    • 利用目标类的接口信息,通过反射创建代理类
    • 可以把代理类固定下来,不因为业务代码量庞大而复杂,便于扩展、修改和维护。
    • 方便实现 RPC 和 AOP
    • 源码难以理解
    更多相关内容
  • Ivoke-WCMDump 什么是Credential Manager ...从Credential Manager导出Windows凭据的Powershell脚本 https://github.com/peewpw/Invoke-WCMDump PS>Import-Module .\Invoke-WCMDump.ps1 ...Invoke-WCMDump
  • invoke

    2021-03-28 18:56:16
    调用 INVOKE的项目描述 这是INVOKE的自动生成的项目描述。 请随时关注更新:-) 执照 所有软件均受版权保护,并受Apache许可证2.0版的保护。
  • 主要介绍了c# Invoke和BeginInvoke 区别分析,需要的朋友可以参考下
  • 主要介绍了详解Java中Method的Invoke方法,需要的朋友可以参考下
  • 主要介绍了Java Method类及invoke方法原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 可以把C/C++中的数据类型、结构体数据格式转换为C#或者VB版本中的对应格式类型。很方便
  • 主要介绍了C#中Invoke 和 BeginInvoke 的真正涵义,需要的朋友可以参考下
  • 主要介绍了Java反射机制及Method.invoke详解,本文讲解了JAVA反射机制、得到某个对象的属性、得到某个类的静态属性、执行某对象的方法、执行某个类的静态方法等内容,需要的朋友可以参考下
  • tag_invoke 我的C ++ 20实现tag_invoke,在WG21论文P1895R0中进行了描述。
  • npm install vue - router - invoke - webpack - plugin - D cnpm cnpm install vue - router - invoke - webpack - plugin - D 纱 yarn add vue - router - invoke - webpack - plugin - D 什么是自动生成路线 ...
  • invoke_impl.rar

    2020-04-24 16:12:02
    std::invoke函数模板的两种实现源码,通过实现invoke,对C++模板编程,SFINAE机制,编译期分支,类型转发,类成员指针都有非常好的理解。第一种实现方式需要C++17,VS2019编译运行通过,第二种方式VS2017上编译运行...
  • $ cd invoke_call $ bundle update $ bundle install $ rake build $ gem install pkg/invoke_call-0.1.0.gem 编译必要的二进制文件 $ rake compile_binaries 可能(可能)会发出大量警告。 只要它不会失败,一切都...
  • Invoke-ARPScan.ps1

    2021-04-04 22:16:05
    Invoke-ARPScan.ps1
  • Invoke-WCMDump-master1.rar

    2021-06-10 12:59:43
    使用 Invoke-WCMDump 加powershell 获取windows凭证密码 使用文档地址:https://blog.csdn.net/caperxi/article/details/117775522
  • Invoke Release是一组命令行工具,可帮助软件工程师快速,轻松且以一致的方式发布Python项目。 它有助于确保您的项目的版本标准在组织的所有项目中都相同,并最大程度地减少了发行期间可能发生的错误。 它使用Git...
  • 并行调用 该函数将接受一个脚本或脚本块,并...# Use Invoke-Parallel with variables in your session $Number = 2 1 .. 10 | Invoke-Parallel - ImportVariables - ScriptBlock { $Number * $_ } # Use the $Usin
  • PAGE PAGE #/ 6 在Invoke或者Beginlnvoke的使用中无一例外地使用了委托 Delegate,至于 委托的本质请参考我的另一随笔 一为什么 Control 类提供了 Invoke 和 Beginlnvoke机制? 1 windows 程序消息机制 WindowsGUl ...
  • 主要介绍了PHP 5.3新增魔术方法__invoke,需要的朋友可以参考下
  • 忽略跨线程访问的错误 CheckForIllegalCrossThreadCalls = false,但是这个方法很不稳定,使用invoke方法解决跨线程访问的问题,里边有2个例子, 通过自己额外创建的线程改变label控件中的内容从而验证invoke方法
  • 主要介绍了InvocationHandler中invoke()方法的调用问题分析,具有一定参考价值,需要的朋友可以了解下。
  • NULL 博文链接:https://liumayulingyan.iteye.com/blog/1900122
  • 让你快速掌握INvoke与BeginInvoke的区别,HAHA
  • 《精通.NET互操作P/Invoke,C++Interop和COM Interop》介绍Windows平台上的托管代码与非托管代码之间进行互操作的各种技术,包括由.NET提供的各种互操作方法、属性以及各种工具的用法及其工作原理。《精通.NET互操作...
  • invoke回调

    2013-11-25 16:05:16
    讲解java的相关设计,回调的方法,一些思想。dengdeng zzzzz
  • invoke.hpp:小时

    2021-05-28 20:05:04
    invoke.hpp std :: invoke / std :: apply for C ++ 11/14 要求 > = 4.9 > = 3.8 > = 2015年 安装 是仅标头的库。 您需要做的就是将headers文件从headers目录复制到您的项目中,并包括它们: # include " ...
  • Invoke-Mimikatz.txt

    2019-03-01 13:32:26
    Invoke-Mimikatz
  • Invoke反射

    2014-07-30 11:23:41
    本程序实现Invoke反射,内置说明txt文档,可以参考
  • WinForm下多线程配合Invoke函数ping百度和主线程ping百度对比。很容易就可以看出俩者的区别。
  • C#中Invoke的用法

    千次阅读 2021-01-22 14:47:28
    一直对invoke和begininvoke的使用和概念比较混乱,这两天看了些资料,对这两个的用法和原理有了些新的认识和理解。 首先说下,invoke和begininvoke的使用有两种情况: 1. control中的invoke、begininvoke。 2. ...

    一直对invoke和begininvoke的使用和概念比较混乱,这两天看了些资料,对这两个的用法和原理有了些新的认识和理解。

     首先说下,invoke和begininvoke的使用有两种情况:

      1. control中的invoke、begininvoke。

      2. delegrate中的invoke、begininvoke。  

      这两种情况是不同的,我们这里要讲的是第1种。下面我们在来说下.NET中对invoke和begininvoke的官方定义。

      control.invoke(参数delegate)方法:在拥有此控件的基础窗口句柄的线程上执行指定的委托。

      control.begininvoke(参数delegate)方法:在创建控件的基础句柄所在线程上异步执行指定委托。

      根据这两个概念我们大致理解invoke表是同步、begininvoke表示异步

    如果你的后台线程在更新一个UI控件的状态后不需要等待,而是要继续往下处理,那么你就应该使用BeginInvoke来进行异步处理。

    如果你的后台线程需要操作UI控件,并且需要等到该操作执行完毕才能继续执行,那么你就应该使用Invoke。

    我们来做一个测试。

    invoke 例子:

     

    复制代码

    private void button1_Click(object sender, EventArgs e)
    {
                MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"AAA");
                invokeThread = new Thread(new ThreadStart(StartMethod));
                invokeThread.Start();
                string a = string.Empty;
                for (int i = 0; i < 3; i++)      //调整循环次数,看的会更清楚
                {
                    Thread.Sleep(1000);   
                    a = a + "B";
                }
                MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+a);
    }
    
     private void StartMethod()
    {
                MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"CCC");
                button1.Invoke(new invokeDelegate(invokeMethod));  
                MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"DDD");
    }
    
     private void invokeMethod()
    {
                //Thread.Sleep(3000);
                MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEE");
    } 

    复制代码

     

    结论:我们运行后,看下程序的运行顺序,1AAA->3CCC和1BBB->1EEE ->3DDD 。 

    解释:主线程运行1AAA,然后1BBB和子线程3CCC同时执行,然后通过invoke来将invokemethod方法提交给主线程,然后子线 程等待主线程执行,直到主线程将invokemethod方法执行完成(期间必须等待主线程的任务执行完成,才会去执行invoke提交的任务),最后执 行子线程3DDD。

     

    begininvoke 例子:

    复制代码

    private void button1_Click(object sender, EventArgs e)
    {
                MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"AAA");
                invokeThread = new Thread(new ThreadStart(StartMethod));
                invokeThread.Start();
                string a = string.Empty;
                for (int i = 0; i < 3; i++)      //调整循环次数,看的会更清楚
                {
                    Thread.Sleep(1000);   
                    a = a + "B";
                }
                MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+a);
    }
    
     private void StartMethod()
    {
                MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"CCC");
                button1.BeginInvoke(new invokeDelegate(invokeMethod));  
                MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"DDD");
    }
    
     private void beginInvokeMethod()
            {
                //Thread.Sleep(3000);
                MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEEEEEEEEEEE");
            }

    复制代码

     

    结论: 我们运行后看看执行的结果:1AAA->1BBB和3CCC->1EEE和3DDD。

    解释: 主线程运行1AAA,然后1BBB和子线程3CCC同时执行,然后通过begininvoke来将invokemethod方法提交给主线程,然后主线程执行1EEE(主线程自己的任务执行完成), 同时子线程继续执行3DDD。

     

    通过这个两段代码的测试比较,我们会发现其实invoke和begininvoke所提交的委托方法都是在主线程中执行的,其实根据我invoke 和begininvoke的定义我们要在子线程中来看这个问题,在invoke例子中我们会发现invoke所提交的委托方法执行完成后,才能继续执行 DDD;在begininvoke例子中我们会发现begininvoke所提交的委托方法后,子线程讲继续执行DDD,不需要等待委托方法的完成。 那么现在我们在回想下invoke(同步)和begininvoke(异步)的概念,其实它们所说的意思是相对于子线程而言的,其实对于控件的调用总是由 主线程来执行的。我们很多人搞不清这个同步和异步,主要还是因为我们把参照物选错了。其实有时候光看概念是很容易理解错误的。

    解决从不是创建控件的线程访问它

     

    在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界面显示。

    正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过 Invoke 或者 BeginInvoke 去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。

    而所谓的“一面响应操作,一面添加节点”永远只能是相对的,使 UI 线程的负担不至于太大而已,因为界面的正确更新始终要通过 UI 线程去做,我们要做的事情是在工作线程中包揽大部分的运算,而将对纯粹的界面更新放到 UI 线程中去做,这样也就达到了减轻 UI 线程负担的目的了。

    举个简单例子说明下使用方法,比如你在启动一个线程,在线程的方法中想更新窗体中的一个TextBox.. 



    using System.Threading; 

    //启动一个线程 
    Thread thread=new Thread(new ThreadStart(DoWork)); 
    thread.Start(); 

    //线程方法 
    private void DoWork() 

    this.TextBox1.Text="我是一个文本框"; 


    如果你像上面操作,在VS2005或2008里是会有异常的... 

    正确的做法是用Invoke\BeginInvoke

    using System.Threading;
    namespace test
    {
    public partial class Form1 : Form
    {
    public delegate void MyInvoke(string str1,string str2);
    public Form1()
    {
    InitializeComponent();


    }
    public void DoWork()
    {
    MyInvoke mi = new MyInvoke(UpdateForm);
    this.BeginInvoke(mi, new Object[] {"我是文本框","haha"});
    }
    public void UpdateForm(string param1,string parm2)
    {
    this.textBox1.Text = param1+parm2;
    }
    private void button1_Click(object sender, EventArgs e)
    {
    Thread thread = new Thread(new ThreadStart(DoWork));
    thread.Start();
    }
    }
    }
    注意代理的使用!

     

    后面再次补充

     在 WinForm开发过程中经常会用到线程,有时候还往往需要在线程中访问线程外的控件,比如:设置textbox的Text属性等等。如果直接设置程序必 定会报出:从不是创建控件的线程访问它,这个异常。通常我们可以采用两种方法来解决。一是通过设置control的属性。二是通过delegate,而通 过delegate也有两种方式,一种是常用的方式,另一种就是匿名方式。下面分别加以说明.
     

    首先,通过设置control的一个属性值为false.我们可以在Form_Load方法中添加:Control.CheckForIllegalCrossThreadCalls=false;来解决。设置为false表示不对错误线程的调用进行捕获。这样在线程中对textbox的Text属性进行设置时就不会再报错了。
    其次,通过delegate的方法来解决。
    普通的委托方法例如:

    复制代码

    delegate void SafeSetText(string strMsg);
    private void SetText(string strMsg)
    {
     if(textbox1.InvokeRequired)
     {
                SafeSetText objSet=new SafeSetText(SetText);
                textbox1.Invoke(objSet,new object[]{strMsg});
    
     }
     else
     {
       textbox1.Text=strMsg;
     }
    }

    复制代码

    在线程内需要设置textbox的值时调用SetText方法既可。我们还可以采用另一种委托的方式来实现,那就是匿名代理,例如:

    复制代码

    delegate void SafeSetText(string strMsg);
    private void SetText2(string strMsg)
    {
      SafeSetText objSet = delegate(string str)
       {
           textBox1.Text = str;
       }
       textBox1.Invoke(objSet,new object[]{strMsg});
    }

    复制代码

    这样同样可以实现。
    个人觉得还是采用代理好些。

     

    在C# 3.0及以后的版本中有了Lamda表达式,像上面这种匿名委托有了更简洁的写法。.NET Framework 3.5及以后版本更能用Action封装方法。例如以下写法可以看上去非常简洁:

     

    void ButtonOnClick(object sender,EventArgs e)

     

    {

     

        this.Invoke(new Action(()=>

     

        {

     

            button.Text="关闭";

     

        }));

     

    }

     最新:

    Invoke(() =>
    {

     button.Text="关闭";
    });

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 544,786
精华内容 217,914
关键字:

invoke