我正在学习,我想探索一些模式。
我想构建一个Registry组件来维护一些东西的地图,我想提供一个序列化访问它:
目前我最终得到了类似的东西:
type JobRegistry struct {
submission chan JobRegistrySubmitRequest
listing chan JobRegistryListRequest
}
type JobRegistrySubmitRequest struct {
request JobSubmissionRequest
response chan Job
}
type JobRegistryListRequest struct {
response chan []Job
}
func NewJobRegistry() (this *JobRegistry) {
this = &JobRegistry{make(chan JobRegistrySubmitRequest, 10), make(chan JobRegistryListRequest, 10)}
go func() {
jobMap := make(map[string] Job)
for {
select {
case sub := <- this.submission:
job := MakeJob(sub.request) // ....
jobMap[job.Id] = job
sub.response <- job.Id
case list := <- this.listing:
res := make([]Job, 0, 100)
for _, v := range jobMap {
res = append(res, v)
}
list.response <- res
}
/// case somechannel....
}
}()
return
}
基本上,我将每个操作封装在一个包含的结构中 参数和响应通道。
然后我为最终用户创建了辅助方法:
func (this *JobRegistry) List() ([]Job, os.Error) {
res := make(chan []Job, 1)
req := JobRegistryListRequest{res}
this.listing <- req
return <-res, nil // todo: handle errors like timeouts
}
我决定为每种类型的请求使用一个通道,以确保类型安全。
我用这种方法看到的问题是:
当一些参数/返回类型发生变化时,很多样板代码和很多地方需要修改
必须做一些奇怪的事情,比如创建另一个包装器结构,以便从处理程序goroutine中返回错误。 (如果我理解正确没有元组,并且无法在通道中发送多个值,例如多值返回)
所以,我想知道这一切是否有意义,或者只是回到旧锁上。
我确信有人会找到一些使用频道的聪明方法。
答案 0 :(得分:1)
我不完全确定我理解你,但我会尽力回答。
您需要一个执行发送给它的作业的通用服务。您也可能希望作业可序列化。
我们需要的是一个定义通用作业的界面。
type Job interface {
Run()
Serialize(io.Writer)
}
func ReadJob(r io.Reader) {...}
type JobManager struct {
jobs map[int] Job
jobs_c chan Job
}
func NewJobManager (mgr *JobManager) {
mgr := &JobManager{make(map[int]Job),make(chan Job,JOB_QUEUE_SIZE)}
for {
j,ok := <- jobs_c
if !ok {break}
go j.Run()
}
}
type IntJob struct{...}
func (job *IntJob) GetOutChan() chan int {...}
func (job *IntJob) Run() {...}
func (job *IntJob) Serialize(o io.Writer) {...}
更少的代码,大致同样有用。
关于使用腋窝流发出信号错误,您可以随时使用辅助功能。
type IntChanWithErr struct {
c chan int
errc chan os.Error
}
func (ch *IntChanWithErr) Next() (v int,err os.Error) {
select {
case v := <- ch.c // not handling closed channel
case err := <- ch.errc
}
return
}