C回调和非Go线程

huangapple go评论92阅读模式
英文:

C callbacks and non-Go threads

问题

  1. 如何在由Go未创建的线程中调用C中的Go代码?
  2. 我应该给C函数指针分配什么,以便由Go未创建的线程可以调用该指针并进入Go代码?

更新0

  • 我不想使用SWIG。
  • 回调将来自Go尚未见过的线程。根据我所知,cgo/lifepkg/runtime中都没有展示这种行为。
英文:
  1. How does one call Go code in C from threads that weren't created by Go?
  2. What do I assign to a C function pointer such that threads not created by Go can call that pointer and enter into Go code?

Update0

  • I don't want to use SWIG.
  • The callbacks will be coming from threads Go hasn't seen before. Neither cgo/life nor anything in pkg/runtime demonstrates this behaviour AFAICT.

答案1

得分: 5

你可以这样做,但是解决方案相对较慢(在我的机器上每次调用约为22微秒)。
答案是让C代码使用C线程原语与另一个实际运行回调的goroutine进行通信。

我创建了一个Go包,提供了这个功能:<a href="http://code.google.com/p/rog-go/source/browse/exp/callback/">rog-go.googlecode.com/hg/exp/callback</a>。
有一个示例包演示了它的用法<a href="http://code.google.com/p/rog-go/source/browse/exp/example/looper/looper.go">here</a>。该示例演示了从在Go运行时之外创建的线程回调到任意Go闭包。另一个示例是<a href="http://code.google.com/p/rog-go/source/browse/exp/example/event">here</a>。这个示例演示了一个典型的C回调接口,并在其上添加了一个Go回调。

尝试第一个示例:

goinstall rog-go.googlecode.com/hg/exp/example/looper
cd $GOROOT/src/pkg/rog-go.googlecode.com/hg/exp/example/looper
gotest

尝试第二个示例:

goinstall rog-go.googlecode.com/hg/exp/example/event
cd $GOROOT/src/pkg/rog-go.googlecode.com/hg/exp/example/event
gotest

这两个示例都假设pthread可用。当然,这只是一个临时措施,直到cgo修复,但是在C回调中调用任意Go闭包的技术即使在那时也是适用的。

这是callback包的文档:

PACKAGE

package callback
import &quot;rog-go.googlecode.com/hg/exp/callback&quot;

VARIABLES

var Func = callbackFunc

Func保存C回调函数的指针。
当调用时,它在Go上下文中调用提供的函数f,并传递给定的参数。

可以通过首先将其转换为函数指针,然后从C中调用来使用它。
以下是设置回调函数的示例:

//static void (*callback)(void (*f)(void*), void *arg);
//void setCallback(void *c){
//	callback = c;
//}
import &quot;C&quot;
import &quot;rog-go.googlecode.com/hg/exp/callback&quot;

func init() {
	C.setCallback(callback.Func)
}
英文:

You can do this, but the solution is relatively slow (about 22µs per call on my machine).
The answer is for the C code to use C thread primitives to communicate with another goroutine that will actually run the callback.

I have created a Go package that provides this functionality: <a href="http://code.google.com/p/rog-go/source/browse/exp/callback/">rog-go.googlecode.com/hg/exp/callback</a>.
There is an example package demonstrating its use <a href="http://code.google.com/p/rog-go/source/browse/exp/example/looper/looper.go">here</a>. The example demonstrates a call back to an arbitrary Go closure from a thread created outside of the Go runtime. Another example is <a href="http://code.google.com/p/rog-go/source/browse/exp/example/event">here</a>. This demonstrates a typical C callback interface and layers a Go callback on top of it.

To try out the first example:

goinstall rog-go.googlecode.com/hg/exp/example/looper
cd $GOROOT/src/pkg/rog-go.googlecode.com/hg/exp/example/looper
gotest

To try out the second example:

goinstall rog-go.googlecode.com/hg/exp/example/event
cd $GOROOT/src/pkg/rog-go.googlecode.com/hg/exp/example/event
gotest

Both examples assume that pthreads are available. Of course, this is just a stop-gap measure until cgo is fixed, but the technique for calling arbitrary Go closures in a C callback will be applicable even then.

Here is the documentation for the callback package:

PACKAGE

package callback
import &quot;rog-go.googlecode.com/hg/exp/callback&quot;

VARIABLES

var Func = callbackFunc

Func holds a pointer to the C callback function.
When called, it calls the provided function f in a
a Go context with the given argument.

It can be used by first converting it to a function pointer
and then calling from C.
Here is an example that sets up the callback function:

//static void (*callback)(void (*f)(void*), void *arg);
//void setCallback(void *c){
//	callback = c;
//}
import &quot;C&quot;
import &quot;rog-go.googlecode.com/hg/exp/callback&quot;

func init() {
	C.setCallback(callback.Func)
}

答案2

得分: 4

我假设你是指使用gcc编译的C代码?

如果我没记错的话,使用6g+cgo和相关工具要么无法实现,要么无法轻松实现。Go使用不同的调用约定(以及分段堆栈等)。

然而,你可以使用[685]c(甚至[685]a)编写C代码,并使用package·function()轻松调用Go代码(我记得甚至可以调用方法)。请参阅runtime包的源代码以获取示例。

更新:

在更新后重新考虑这个问题,并经过更多思考。使用6c或cgo无法以标准方式实现这一点。特别是因为线程不是由Go运行时启动的,当前的实现将失败。调度器会突然控制一个它不知道的线程;此外,该线程将缺少一些Go运行时用于管理堆栈和其他一些东西的线程本地变量。此外,如果Go函数返回一个值(或多个值),C代码无法在当前支持的平台上访问它们,因为Go将值返回到堆栈上(但你可以使用汇编语言访问它们)。考虑到这些问题,我相信你仍然可以使用通道来实现这一点。这将要求你的C代码与Go运行时的内部工作有一些过于亲密的关系,但对于给定的实现来说,它是可行的。虽然使用通道可能不是你寻找的解决方案,但它可能更符合Go的概念比回调函数。如果你的C代码重新实现了通道实现中的至少发送方法(该代码是为6c编写的,所以可能需要适应gcc,并且它调用了Go运行时,我们已经确定无法从非Go线程中调用它),你应该能够锁定通道并向其推送一个值。Go调度器可以继续管理自己的线程,但现在它可以从在C中启动的其他线程接收数据。

诚然,这是一个hack;我没有仔细研究过,但可能需要一些其他的hack才能使其工作(我相信通道本身维护了等待它们的goroutine的列表[编辑:确认:runtime·ready(gp);],所以你的Go代码需要一些东西来唤醒接收通道或确保在你已经推送值之前,Go代码不会从通道接收)。然而,我看不到任何理由这个方法行不通,而使用在C中创建的线程运行6g生成的代码则是明确行不通的。

我的原始答案仍然有效:除非语言或运行时有所添加,否则目前还无法以你期望的方式实现(我很乐意被证明是错误的)。

英文:

I'll assume you mean from C code compiled with gcc?

IIRC, this either can't be done or can't easily be done using 6g+cgo and friends. Go uses a different calling convention (as well as the segmented stacks and such).

However, you can write C code for [685]c (or even [685]a) and call into go easily using package·function() (you can even call methods IIRC). See the Source of the <code>runtime</code> package for examples.

<h2>Update:</h2>

Coming back to this question after the update, and giving it some more thought. This can't be done in a standard fashion using 6c or cgo. Especially because the threads are not started by the go runtime, the current implementation would fail. The scheduler would suddenly have a thread under its control that it does not know about; additionally, that thread would be missing some thread-local variables the go runtime uses for managing stacks and some other things. Also, if the go function returns a value (or several) the C code can't access it on the currently supported platforms, as go returns values on the stack (you could access them with assembly though). With these things in mind, I do believe you could still do this using channels. It would require your C code to be a little too intimate with the inner workings of the go runtime, but it would work for a given implementation. While using channels may not be the solution you're looking for, it could possibly fit more nicely with the concepts of Go than callbacks. If your C code reimplemented at least the sending methods in The channel implementation (that code is written for 6c, so it would have to be adapted for gcc most likely, and it calls the go runtime, which we've determined can't be done from a non-go thread), you should be able to lock the channel and push a value to it. The go scheduler can continue to manage it's own threads, but now it can receive data from other threads started in C.

Admittedly, it's a hack; I haven't looked close enough, but it would probably take a few other hacks to get it working (I believe the channels themselves maintain a list of the goroutines that are waiting on them [EDIT: confirmed: runtime&#183;ready(gp);], so you'd need something in your go code to wake up the receiving channel or to warranty the go code won't receive on the channel until you've already pushed a value). However, I can't see any reason this can't work, whereas there are definite reasons that running code generated by 6g on a thread created in C can't.

My original answer still holds though: barring an addition to the language or runtime, this can't yet be done the way you'd like (I'd love to be proven wrong here).

答案3

得分: 3

你可以在这些绑定PortAudio音频I/O库的rog回调包的实际应用中找到:http://code.google.com/p/portaudio-go/。这可能会让理解更容易。

(感谢你实现了这个,rog。这正是我所需要的!)

英文:

You can find a real-world application of rog's callback package in these bindings for the PortAudio audio I/O library: http://code.google.com/p/portaudio-go/. Might make it easier to understand..

(Thanks for implementing that, rog. It's just what I needed!)

huangapple
  • 本文由 发表于 2010年11月30日 18:51:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/4312894.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定