libgo icon indicating copy to clipboard operation
libgo copied to clipboard

co_sched.Start( ); 是阻塞的

Open chinesejie opened this issue 5 years ago • 30 comments

co_sched.Start( ); 是阻塞的,能不能 运行完了就不阻塞呢?

chinesejie avatar Apr 07 '19 13:04 chinesejie

goStart是不阻塞当前线程的

yyzybb537 avatar Apr 08 '19 02:04 yyzybb537

goStart不阻塞但是也没等协程都运行结束呢。 以前那个RunUntilNoTask函数怎么没了呢? 它两效果不一样吧

chinesejie avatar Apr 08 '19 02:04 chinesejie

我说下我现在的情况吧, 1)只有libgo2.6才支持RunUntilNoTask函数,这个才能让我们老业务代码复用创建过的线程,但是我还想用boost来提升性能,不过boost_1-61_0的版本明显out了,不支持Boost.Coroutine2 2)我使用最新版本的libgo3.x,可惜 没有RunUntilNoTask函数,goStart不满足RunUntilNoTask的需求,stop了就不能重新start。所以还缺一个RunUntilNoTask 还有好的建议吗?

chinesejie avatar Apr 08 '19 11:04 chinesejie

看起来你是按照"线程池"的方式在使用了,那么你不需要关注"调度线程"的启停,只需要关注每个"协程任务"是否完成就行了,没有任务的时候,调度线程自然会进入等待状态的。

yyzybb537 avatar Apr 09 '19 01:04 yyzybb537

你说得对,老业务代码自己维护了一个线程池。我怎么让调度线程没有任务就不阻塞,而不是等待呢。目前我改了你的代码,在stop的时候解锁start_ lock,在start的时候设置stop变量为false

chinesejie avatar Apr 09 '19 01:04 chinesejie

不阻塞的时候 你希望调度线程干啥活呢?

yyzybb537 avatar Apr 09 '19 01:04 yyzybb537

其实是在libgo里面做到了不阻塞,这样我们的改动成本最低。调用了类似rununtilnotask之后,不阻塞运行到了我的老业务逻辑的线程池里面,拿不到下一个用户任务还是被阻塞了。

chinesejie avatar Apr 09 '19 02:04 chinesejie

看起来似乎是libgo调度器被放到你老的线程池中被当做task来执行了?

yyzybb537 avatar Apr 09 '19 02:04 yyzybb537

对,就是这样的。我们有自己的线程池调度,借助了lingo的线程内协程并发功能,跟hook异步。

chinesejie avatar Apr 09 '19 02:04 chinesejie

听起来goStart可以解决你的问题呀,既然都是当做线程池来用,libgo的调度线程是否和原线程池共用线程是无关紧要的事情,分别使用不同的线程也是可以的吧?

yyzybb537 avatar Apr 09 '19 02:04 yyzybb537

问题就在这里,用了gostart,至少要启动一个新的调度线程,而不是用我线程池里面我自己调度的那个线程。我那个自己调度的线程里面有很多threadlocal的数据,gostart的话业务代码跑不起来

chinesejie avatar Apr 09 '19 02:04 chinesejie

这样啊,那你可以这样: 1.启动新线程 2.做threadlocal数据的初始化工作 3.调用Start(1)

yyzybb537 avatar Apr 09 '19 02:04 yyzybb537

每个线程的threadlocal的init成本很高的,每次初始化伤不起。必须在整个程序启动的时候就在我们那个线程池对每个线程就init好了,这也是我不得不这么做的原因了。

chinesejie avatar Apr 09 '19 02:04 chinesejie

很高的原因是什么?

yyzybb537 avatar Apr 09 '19 02:04 yyzybb537

这个由业务决定,有很多路子系统请求,每路子请求召回上千个。所以我们这个自己调度的线程池的线程里面的 每个 vector都是 一次性reserve好,在init的时候emplace了上千个,然后坚决不用resize ,数据来的时候直接写到vector的位置,使用了 额外变量valid_length来管理这个容器。 假如每次都去创建这些threadlocal数据,大概会是每个线程有几百k的堆数据。

chinesejie avatar Apr 09 '19 02:04 chinesejie

1.看起来这种初始化只需要做一次, 然后每个线程都copy一份到自己的tls即可 2.你是嵌入式开发吗? 几百K的堆数据对于现代server来讲应该无足轻重吧?

yyzybb537 avatar Apr 09 '19 02:04 yyzybb537

1)每个线程copy一次,这个有copy成本吧? 我们开发不是应该尽量做到只寻址,少创建或者copy吗? 2)初始化数据是因为没有塞字段,所以是几百k,字段塞进去估计就十几M了。不过塞过的字段每次请求完了可以扔掉。

chinesejie avatar Apr 09 '19 02:04 chinesejie

因为系统 是关注耗时。必须每次请求在几十ms处理完。

chinesejie avatar Apr 09 '19 02:04 chinesejie

这个不是tls数据的初始化工作吗, 初始化阶段的copy快点慢点无所谓吧, 后续来请求的时候已经有tls数据了 就不用copy了

yyzybb537 avatar Apr 09 '19 02:04 yyzybb537

现在你的线程池 启动了多少个线程?

yyzybb537 avatar Apr 09 '19 02:04 yyzybb537

针对你说的这句话:”后续来请求的时候已经有tls数据“,但是libgo的gostart 的线程都是新创建的吧,没法提前init好。 现在你的线程池 启动了多少个线程? 300-400个

chinesejie avatar Apr 09 '19 02:04 chinesejie

goStart都是内部创建的不错, Start(1)是直接使用当前线程做调度,这个是可以自己控制的

yyzybb537 avatar Apr 09 '19 02:04 yyzybb537

Start(1)等同于Start()吧?这样就回到原来的问题了,Start() 是阻塞,我没task了,然后当前线程还在那里等,我的老业务的线程池没法调度他。

chinesejie avatar Apr 09 '19 02:04 chinesejie

因为系统 是关注耗时。必须每次请求在几十ms处理完。

我们现在每个同步线程池里面开了很多异步线程 去访问子系统,这样导致整个系统的线程有几千个。而每个同步线程里面的异步线程其实可以全部改成libgo的协程,因为这些异步线程基本都是io的。

这也是我们想通过libgo来改进我们的系统的最原始的考虑。

chinesejie avatar Apr 09 '19 02:04 chinesejie

嗯 大概了解了,那你初始化的TLS数据,关注时效性吗? 比如说进程启动的时候做一次初始化,然后后续使用的时候都用这一份,会不会有问题?

yyzybb537 avatar Apr 09 '19 03:04 yyzybb537

万谢,这个初始化的tls数据 不需要关注时效性。 1)某些时效性,有专门的double buffer 去更新这块内存,也就是tls里面的数据是double buffer,有时效性 reload的时候 会更新,切换到最新的buffer那路即可 2)其他 时效性的数据 都是由下游返回的数据 来维持的。

chinesejie avatar Apr 09 '19 03:04 chinesejie

vector* initTLS() { vector* p = new vector; *p = copy_from(); return p; }

vector & getTLS() { static thread_local vector *p = initTLS(); return *p; }

那你在使用tls数据的时候,可以调用这样一个getTLS函数

yyzybb537 avatar Apr 09 '19 03:04 yyzybb537

我觉得库还是不应该自己启动线程,应该让外部来启动

mlkt avatar Apr 17 '19 07:04 mlkt

调度器stop的时候没有等所有的协程任务处理完成,这样会导致任务丢失,是否不合理呢? @yyzybb537 等任务处理完成以后再退出会不会更好?

terryyf avatar Oct 25 '19 09:10 terryyf

我感觉是需要提供一个类似go里面的waitgroup的组件。

dearbird avatar Nov 20 '19 00:11 dearbird