如果需要将CUDA编写的dll与其他程序语言编写的项目结合,如窗体界面都采用C#进行编写,核心计算采用CUDA编写并编译成DLL,则只需要在C#项目里面引用这个DLL并调用相应的函数即可。下面仍然以项目Test00302中编译的DLL为例,新建一个名为Test00304的Windows窗体应用程序(Visual C#),如下图所示:
在窗体中添加一个按钮,如下图所示:
然后添加名称空间System.Runtime.InteropServices的引用,并导入外部的DLL文件(Test00302.dll),同时定义外部调用的函数vectorAdd(),最后为按钮的Click事件添加调用函数vectorAdd()的代码,Form1.cs文件的内容如下:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; namespace Test00304 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } //引用外部DLL [DllImport("Test00302.dll")] public static extern int vectorAdd(int[] c, int[] a, int[] b, int size); private void button1_Click(object sender, EventArgs e) { string msg = ""; int[] c = new int[5]; int[] a = new int[5] { 1, 2, 3, 4, 5 }; int[] b = new int[5] { 10, 20, 30, 40, 50 }; const int size = 5; int r = vectorAdd(c, a, b, size); if (r == 0) { msg = "计算成功\n"; msg += "计算结果:\n"; for (int i = 0; i < size; i++) msg +=c[i].ToString()+","; } else msg = "计算失败"; MessageBox.Show(msg); } } }
然后复制项目Test00302中编译的的Test00302.dll 文件到项目Test00304路径下debug文件夹中,如下图所示:
然后运行C#项目,点击按钮,开始执行CUDADLL中的函数,如果C#项目是基于.NetFramework 4.0,可能会出现如下提示消息:
检测到 PInvokeStackImbalance
Message: 对 PInvoke 函数“Test00304!Test00304.Form1::vectorAdd”的调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配。
如下图所示:
由于在C#项目中调用DLL中函数vectorAdd()参数以及参数类型都与原函数一致,所以不存在数据类型不一致的问题,如果忽略该问题也可以继续运行。但是如果将项目的目标框架设置成.Net Framework 3.5,如下图所示,即不存在上面的“检测到PInvokeStackImbalance”问题:
运行程序,其结果如下图所示:
本书目前在github上由laruence(http://www.laruence.com)和walu(http://www.walu.cc)两位大牛组织翻译. 该翻译项目地址为: https://github.com/walu/phpbook
原书名: <Extending and Embedding PHP>
原作者: Sara Golemon
译者: goosman.lei(雷果国)
译者Email: lgg860911@yahoo.com.cn
译者Blog: http://blog.csdn.net/lgg201
php的生命周期
在常见的webserver环境中, 你不能直接启动php解释器; 一般是启动apache或其他webserver, 由它们加载php处理需要处理的脚本(请求的.php文档).
一切都从sapi开始
尽管看起来有所不同, 但实际上CLI的行为和web方式一致. 在命令行中键入php命令将启动"命令行sapi", 它实际上就像一个设计用于服务单请求的迷你版webserver. 当脚本运行完成后, 这个迷你的php-webserver终止并返回控制给shell.
启动和终止
这里的启动和终止过程分为两个独立的启动阶段和两个独立的终止阶段. 一个周期用于php解释器整体执行所需结构和值的初始化设置, 它们在sapi生命周期中持久存在. 另一个则仅服务于单页面请求, 生命周期短暂一些.
初始化启动在所有的请求发生之前, php调用每个扩展的MINIT(模块初始化)方法. 这里, 扩展可能会定义常量, 定义类, 注册资源, 流, 过滤处理器等所有将要被请求脚本所使用的资源. 所有这些都有一个特性, 就是它们被设计跨所有请求存在, 也可以称为"持久".
常见的MINIT方法如下:
/* 初始化myextension模块 * 这在sapi启动后将立即发生 */ PHP_MINIT_FUNCTION(myextension) { /* 全局: 第12章 */ #ifdef ZTS ts_allocate_id(&myextension_globals_id, sizeof(php_myextension_globals), (ts_allocate_ctor) myextension_globals_ctor, (ts_allocate_dtor) myextension_globals_dtor); #else myextension_globals_ctor(&myextension_globals TSRMLS_CC); #endif /* REGISTER_INI_ENTRIES() 指向一个全局的结构, 我们将在第13章"INI设置"中学习 */ REGISTER_INI_ENTRIES(); /* 等价于define('MYEXT_MEANING', 42); */ REGISTER_LONG_CONSTANT("MYEXT_MEANING", 42, CONST_CS | CONST_PERSISTENT); /* 等价于define('MYEXT_FOO', 'bar'); */ REGISTER_STRING_CONSTANT("MYEXT_FOO", "bar", CONST_CS | CONST_PERSISTENT); /* 资源: 第9章 */ le_myresource = zend_register_list_destructors_ex( php_myext_myresource_dtor, NULL, "My Resource Type", module_number); le_myresource_persist = zend_register_list_destructors_ex( NULL, php_myext_myresource_dtor, "My Resource Type", module_number); /* 流过滤器: 第16章 */ if (FAILURE == php_stream_filter_register_factory("myfilter", &php_myextension_filter_factory TSRMLS_CC)) { return FAILURE; } /* 流包装器: 第15章 */ if (FAILURE == php_register_url_stream_wrapper ("myproto", &php_myextension_stream_wrapper TSRMLS_CC)) { return FAILURE; } /* 自动全局变量: 第12章 */ #ifdef ZEND_ENGINE_2 if (zend_register_auto_global("_MYEXTENSION", sizeof("_MYEXTENSION") - 1, NULL TSRMLS_CC) == FAILURE) { return FAILURE; } zend_auto_global_disable_jit ("_MYEXTENSION", sizeof("_MYEXTENSION") - 1 TSRMLS_CC); #else if (zend_register_auto_global("_MYEXTENSION", sizeof("_MYEXTENSION") - 1 TSRMLS_CC) == FAILURE) { return FAILURE; } #endif return SUCCESS; }
在一个请求到达时, php会安装一个操作环境, 该环境包含符号表(变量存储), 并且会同步每个目录的配置值. php接着遍历所有的扩展, 这一次调用每个扩展的RINIT(请求初始化)方法. 这里, 扩展可能充值全局变量到默认值, 预置变量到脚本的符号表, 或执行其他的任务比如记录页面请求日志到文件. RINIT之于所有脚本请求就像auto_prepend_file指令一样.
RINIT方法的写法如下:
/* 每个页面请求开始之前执行 */ PHP_RINIT_FUNCTION(myextension) { zval *myext_autoglobal; /* 初始化MINIT函数中定义的自动全局变量为空数组. 这等价于$_MYEXTENSION = array(); */ ALLOC_INIT_ZVAL(myext_autoglobal); array_init(myext_autoglobal); zend_hash_add(&EG(symbol_table), "_MYEXTENSION", sizeof("_MYEXTENSION") - 1, (void**)&myext_autoglobal, sizeof(zval*), NULL); return SUCCESS; }
在一个请求完成处理后(到达脚本文件末尾或调用了die()/exit()语句), php通过调用每个扩展的RSHUTDOWN(请求终止)开始清理过程. 就像RINIT对应于auto_prepend_file, RSHUTDOWN可以类比auto_append_file指令. 在RSHUTDOWN和auto_append_file之间, 最重要的不同是: 无论如何, RSHUTDOWN总会被执行, 而用户空间脚本的die()/exit()调用会跳过所有的auto_append_file.
在符号表和其他资源释放之前所需要做的最后一件事旧是RSHUTDOWN. 在所有的RSHUTDOWN方法完成后, 符号表中的所有变量都会被立即unset(),
在此期间, 所有非持久化资源和对象的析构器都将被调用去优雅的释放资源.
/* 每个页面请求结束后调用 */ PHP_RSHUTDOWN_FUNCTION(myextension) { zval **myext_autoglobal; if (zend_hash_find(&EG(symbol_table), "_MYEXTENSION", sizeof("_MYEXTENSION"), (void**)&myext_autoglobal) == SUCCESS) { /* 做一些对$_MYEXTENSION数组的值有意义的处理 */ php_myextension_handle_values(myext_autoglobal TSRMLS_CC); } return SUCCESS; }
最后, 当所有的请求都被满足(完成处理)后, webserver或其他sapi就开始准备终止, php循环执行每个扩展的MSHUTDOWN(模块终止)方法. 这是MINIT周期内, 扩展最后一次卸载处理器和释放持久化分配的内存的机会.
/* 这个模块正在被卸载, 常量和函数将被自动的卸载, 持久化资源, 类, 流处理器必须手动的卸载. */ PHP_MSHUTDOWN_FUNCTION(myextension) { UNREGISTER_INI_ENTRIES(); php_unregister_url_stream_wrapper ("myproto" TSRMLS_CC); php_stream_filter_unregister_factory ("myfilter" TSRMLS_CC); return SUCCESS; }
生命周期
每个php实例, 无论从init脚本启动还是从命令行启动, 接下来都是上一节讲到的一系列请求/模块的初始化/终止事件, 以及
行为型模式:
它定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户。
简单工厂模式的不足:
由于工厂本身包括了所有的收费方式,商场是可能经常性的更改打折额度和返利额度,每次维护或扩展收费方法都要改动这个工厂,以致代码需重新编译部署,很麻烦
方法:
1,定义一个所有支持的算法的公共接口
2,将所有具体算法或行为封装在一起
3,维护对公共接口的引用
客户端代码是将所有的算法实例化
这个模式涉及到三个角色:
环境(Context)角色:持有一个Strategy类的引用。
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为
优点:
使用策略模式可以把行为和环境分割开来。环境类负责维持和查询行为类,各种算法则在具体策略类(ConcreteStrategy)中提供。由于算法和环境独立开来,算法的增减、修改都不会影响环境和客户端。当出现新的促销折扣或现有的折扣政策出现变化时,只需要实现新的策略类,并在客户端登记即可。策略模式相当于"可插入式(Pluggable)的算法"
个人理解:
策略模式与工厂模式
工厂模式是将需要实例化的所有对象放到一个类中,然后在客户端直接使用工厂的方法,实现不同对象的不同操作
策略模式是将工厂中的方法提取出来,然后将需要实例化的对象都放到了客户端去实例。当我需要添加不同的策略时,只在客户端添加它的实例即可,然后将对象作为参数传到策略模式中即可
策略模式结构图:
课本实例:商场打折
代码如下:
class cashcontext { //基类类型的对像 private CashSuper cs; //初始化时(构造函数),传入具体的对象 public cashcontext(CashSuper csuper) { this.cs = csuper; //将csuper赋给cs,然后计算cs对象的收到的现金数 //通过传入参数的不同,计算不同方式的收到现金数 } public double getresult(double money) //根据具体的策略对象,调用其算法的方法 { return cs.acceptCash(money); } }
备忘录模式
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态.
Memento模式比较适用于功能比较 复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态
课本实例:游戏的状态保存
代码如下:
//一个发起人类:有攻击力,生命力,还有创建备忘和恢复备忘的功能等 class GameRole { //生命力 private int vit; public int Vitality { get { return vit; } set { vit = value; } } //攻击力 private int atk; public int Attack { get { return atk ; } set { atk = value; } } //防御类 private int def; public int Defense { get { return def ; } set { def = value; } } //状态显示 public void StateDisplay() { Console.WriteLine("角色当前状态:"); Console.WriteLine("体力:{0}", this.vit); Console.WriteLine("攻击力:{0}", this.atk); Console.WriteLine("防御类:{0}", this.def); Console.WriteLine(""); } //获得初始状态 public void GetInitState() { this.vit = 100; this.atk = 100; this.def = 100; } //战斗 public void Fight() { this.vit = 0; this.atk = 0; this.def = 0; } //保存角色状态 public RoleStateMemento SaveState() { //保存状态时,创建存储箱 return (new RoleStateMemento(vit, atk, def)); } //恢复角色状态 public void RecoveryState(RoleStateMemento memento) { this.vit = memento.Vitality ; this.atk = memento .Attack ; this.def = memento.Defense ; } } //一个备忘录类:将要备忘的内容存储在备忘录中 //角色状态存储箱 class RoleStateMemento { private int vit; private int atk; private int def; //以上是角色状态存储箱的一些属性 public RoleStateMemento(int vit, int atk, int def) { this.vit = vit; this.atk = atk; this.def = def; } //生命力 public int Vitality { get { return vit; } set { vit = value; } } //攻击力 public int Attack { get { return atk ; } set {atk = value; } } //防御力