PHP 混合 Go 协程并发
jinpengxx8 · · 7620 次点击 · · 开始浏览想法很简单。通过设置runtime.GOMAXPROCS(1)让
golang 的进程变成单线程执行的。类似python用gevent的效果。然后通过调度多个协程实现异步I/O并发。php作为一个子函数跑在go的进程内,php需要yield到其他协程时,通过回调到golang函数来实现。从php里调用go提供的子函数时,go保证保存php的当前上下文。当协程执行权让渡回来的时候,把原来的php上下文恢复。关键的代码在:
// 保存当前协程上的php上下文 oldServerCtx := engine.ServerContextGet() fmt.Println(oldServerCtx) defer engine.ServerContextSet(oldServerCtx) oldExecutorCtx := engine.ExecutorContextGet() fmt.Println(oldExecutorCtx) defer engine.ExecutorContextSet(oldExecutorCtx) oldCoreCtx := engine.CoreContextGet() fmt.Println(oldCoreCtx) defer engine.CoreContextSet(oldCoreCtx) // 放弃全局的锁,使得其他的协程可以开始执行php engineLock.Unlock() defer engineLock.Lock()
ServerContextGet 这几个函数是我加的,获得的是php的(EG/SG/PG)这三个全局context(参见:http://www.cnblogs.com/chance...)。修改过的php-go的源代码在:https://github.com/taowen/go-...
完整的php/go混合协程的demo:
package main import ( "fmt" "github.com/deuill/go-php/engine" "os" "runtime" "time" "sync" ) type TestObj struct{} func newTestObj(args []interface{}) interface{} { return &TestObj{} } var engineLock *sync.Mutex func (self *TestObj) Hello() { oldServerCtx := engine.ServerContextGet() fmt.Println(oldServerCtx) defer engine.ServerContextSet(oldServerCtx) oldExecutorCtx := engine.ExecutorContextGet() fmt.Println(oldExecutorCtx) defer engine.ExecutorContextSet(oldExecutorCtx) oldCoreCtx := engine.CoreContextGet() fmt.Println(oldCoreCtx) defer engine.CoreContextSet(oldCoreCtx) engineLock.Unlock() defer engineLock.Lock() time.Sleep(time.Second) fmt.Println("sleep done") } func main() { runtime.GOMAXPROCS(1) theEngine, err := engine.New() engineLock = &sync.Mutex{} if err != nil { fmt.Println(err) } _, err = theEngine.Define("TestObj", newTestObj) wg := &sync.WaitGroup{} wg.Add(2) before := time.Now() fmt.Println("1") go func() { engineLock.Lock() defer engineLock.Unlock() context1, err := theEngine.NewContext() if err != nil { fmt.Println(err) } context1.Output = os.Stdout if err != nil { fmt.Println(err) } fmt.Println("1 enter") _, err = context1.Eval("$testObj = new TestObj(); $testObj->Hello();") fmt.Println("1 back") if err != nil { fmt.Println(err) } //theEngine.DestroyContext(context1) fmt.Println("1 done") wg.Done() }() fmt.Println("2") go func() { engineLock.Lock() defer engineLock.Unlock() context2, err := theEngine.NewContext() if err != nil { fmt.Println(err) } if err != nil { fmt.Println(err) } context2.Output = os.Stdout fmt.Println("2 enter") _, err = context2.Eval("$testObj = new TestObj(); $testObj->Hello();") fmt.Println("2 back") if err != nil { fmt.Println(err) } //theEngine.DestroyContext(context2) fmt.Println("2 done") wg.Done() }() wg.Wait() after := time.Now() fmt.Println(after.Sub(before))
有疑问加站长微信联系(非本文作者)
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
关注微信- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码` - 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传
收入到我管理的专栏 新建专栏
想法很简单。通过设置runtime.GOMAXPROCS(1)让
golang 的进程变成单线程执行的。类似python用gevent的效果。然后通过调度多个协程实现异步I/O并发。php作为一个子函数跑在go的进程内,php需要yield到其他协程时,通过回调到golang函数来实现。从php里调用go提供的子函数时,go保证保存php的当前上下文。当协程执行权让渡回来的时候,把原来的php上下文恢复。关键的代码在:
// 保存当前协程上的php上下文 oldServerCtx := engine.ServerContextGet() fmt.Println(oldServerCtx) defer engine.ServerContextSet(oldServerCtx) oldExecutorCtx := engine.ExecutorContextGet() fmt.Println(oldExecutorCtx) defer engine.ExecutorContextSet(oldExecutorCtx) oldCoreCtx := engine.CoreContextGet() fmt.Println(oldCoreCtx) defer engine.CoreContextSet(oldCoreCtx) // 放弃全局的锁,使得其他的协程可以开始执行php engineLock.Unlock() defer engineLock.Lock()
ServerContextGet 这几个函数是我加的,获得的是php的(EG/SG/PG)这三个全局context(参见:http://www.cnblogs.com/chance...)。修改过的php-go的源代码在:https://github.com/taowen/go-...
完整的php/go混合协程的demo:
package main import ( "fmt" "github.com/deuill/go-php/engine" "os" "runtime" "time" "sync" ) type TestObj struct{} func newTestObj(args []interface{}) interface{} { return &TestObj{} } var engineLock *sync.Mutex func (self *TestObj) Hello() { oldServerCtx := engine.ServerContextGet() fmt.Println(oldServerCtx) defer engine.ServerContextSet(oldServerCtx) oldExecutorCtx := engine.ExecutorContextGet() fmt.Println(oldExecutorCtx) defer engine.ExecutorContextSet(oldExecutorCtx) oldCoreCtx := engine.CoreContextGet() fmt.Println(oldCoreCtx) defer engine.CoreContextSet(oldCoreCtx) engineLock.Unlock() defer engineLock.Lock() time.Sleep(time.Second) fmt.Println("sleep done") } func main() { runtime.GOMAXPROCS(1) theEngine, err := engine.New() engineLock = &sync.Mutex{} if err != nil { fmt.Println(err) } _, err = theEngine.Define("TestObj", newTestObj) wg := &sync.WaitGroup{} wg.Add(2) before := time.Now() fmt.Println("1") go func() { engineLock.Lock() defer engineLock.Unlock() context1, err := theEngine.NewContext() if err != nil { fmt.Println(err) } context1.Output = os.Stdout if err != nil { fmt.Println(err) } fmt.Println("1 enter") _, err = context1.Eval("$testObj = new TestObj(); $testObj->Hello();") fmt.Println("1 back") if err != nil { fmt.Println(err) } //theEngine.DestroyContext(context1) fmt.Println("1 done") wg.Done() }() fmt.Println("2") go func() { engineLock.Lock() defer engineLock.Unlock() context2, err := theEngine.NewContext() if err != nil { fmt.Println(err) } if err != nil { fmt.Println(err) } context2.Output = os.Stdout fmt.Println("2 enter") _, err = context2.Eval("$testObj = new TestObj(); $testObj->Hello();") fmt.Println("2 back") if err != nil { fmt.Println(err) } //theEngine.DestroyContext(context2) fmt.Println("2 done") wg.Done() }() wg.Wait() after := time.Now() fmt.Println(after.Sub(before))