可以试下 oss 的 x-oss-process
有兴趣可以看下 https://ruby-china.org/topics/29041 我 16 年的帖子
用了 fiber, 会希望所有的 io 操作都能基于 fiber 调度,mysql 这块重头戏,这块整体底层的 io 调度如果不支持 fiber 调度,那 fiber 带来的整体的改进,大概和也就和 eventmachine 差不多...
mysql 的 c 库里面,调用 io 操作的时候会有 global vm lock 的锁的 (之前看的时候还是这样)
好久没关注 Fiber 了... mysql2 这样的大热门的 Gem 有生产可用的 fiber 实现了么?没有的话...很难推广...
这么多年啦... 好久没关注了
zk, etcd 都可以
基于 zk 的 watch 机制,可能会更好,而且,也算是分布式的解决方案
哥们,果然你很活跃呀
你这个代码的线程调度是不会影响 @a
和 @c
的值得,可以用这个代码来验证
@a = 1
r = []
10.times do |e|
Thread.new {
@c = 1
@c += @a
r << [e, @c]
}
end
r 里面的值,虽然 e 的前后顺序不一样,但是,@c
的值是一致的
但是你在@c=1
和 @c += @a
中间加一个 puts 的话,这个就会触发 gil 的 lock, 数据异常了
@a = 1
r = []
10.times do |e|
Thread.new {
@c = 1
puts 1
@c += @a
r << [e, @c]
}
end
你这个答案是靠谱的,其实题主最后一个代码是不存在线程安全问题的
GIL 的工作机制不像大家想象的一样,在 ruby 代码的任何一个点都会切换到另外一个线程去工作
而是有几个明确的工作点的
static void
gvl_acquire_common(rb_vm_t *vm)
{
if (vm->gvl.acquired) {
vm->gvl.waiting++;
if (vm->gvl.waiting == 1) {
/*
* Wake up timer thread iff timer thread is slept.
* When timer thread is polling mode, we don't want to
* make confusing timer thread interval time.
*/
rb_thread_wakeup_timer_thread_low();
}
while (vm->gvl.acquired) {
native_cond_wait(&vm->gvl.cond, &vm->gvl.lock);
}
vm->gvl.waiting--;
if (vm->gvl.need_yield) {
vm->gvl.need_yield = 0;
native_cond_signal(&vm->gvl.switch_cond);
}
}
vm->gvl.acquired = 1;
}
static void
gvl_acquire(rb_vm_t *vm, rb_thread_t *th)
{
native_mutex_lock(&vm->gvl.lock);
gvl_acquire_common(vm);
native_mutex_unlock(&vm->gvl.lock);
}
static void
gvl_release_common(rb_vm_t *vm)
{
vm->gvl.acquired = 0;
if (vm->gvl.waiting > 0)
native_cond_signal(&vm->gvl.cond);
}
static void
gvl_release(rb_vm_t *vm)
{
native_mutex_lock(&vm->gvl.lock);
gvl_release_common(vm);
native_mutex_unlock(&vm->gvl.lock);
}
这几个方法是用来控制 gil 的锁的,可以参考一下 gil 在 c 里面的调用情况
有兴趣的话答个题我们来聊聊
额,这个重点从来不是你答的什么,而是从你答得什么中我们聊出来的那些
根据您的面试题的代码,我们才可以更好展开了解您的技能域 也让面试不变的太清谈么
#29 楼 @adam_chen 手机型号是啥,android 版本是啥?
如果只是为了解决 assets precompile 的话...干净整洁,也不用编译这么大的一个 V8
我觉得如果可以接受的话,不如在机器上装个 nodejs,然后去掉 therubyracer
#5 楼 @42thcoder mac 确实不可信,
咦,露脸了...O(∩_∩)O 哈哈~
#16 楼 @mizuhashi em 有纯 ruby 实现的,也有 java 和 c 实现的,默认基本是 C 实现的
#13 楼 @mizuhashi 不一样,你这个是线程和 FIBER 的整合,而且后面用 while 的方式等待 fiber 执行完非常没有效率
EM 维护了一个 event loop 当 Fiber 执行完毕了,会进入 EM 的 event loop 唤醒 fiber
#9 楼 @rogerluo410 这个是典型 java 的 netty 的方式,但是 ruby 有 GIL,效果不太好
if (th->fiber) {
GetFiberPtr(th->fiber, fib);
cont_save_thread(&fib->cont, th);
}
else {
/* create current fiber */
fib = root_fiber_alloc(th);
th->root_fiber = th->fiber = fib->cont.self;
}
这是它 fiber store 里面的实现,那么,确实,root fiber 是在第一个 fiber resume 的时候创建的
static rb_fiber_t *
root_fiber_alloc(rb_thread_t *th)
{
rb_fiber_t *fib;
/* no need to allocate vm stack */
fib = fiber_t_alloc(fiber_alloc(rb_cFiber));
fib->cont.type = ROOT_FIBER_CONTEXT;
#if FIBER_USE_NATIVE
#ifdef _WIN32
fib->fib_handle = ConvertThreadToFiber(0);
#endif
#endif
fib->status = RUNNING;
fib->prev_fiber = fib->next_fiber = fib;
return fib;
}
在 windows 上会有非常明显的 ConvertThreadToFiber 的调用,因次,我才说会进入一个主 fiber 的模式
3.我试过,兼容性不太好.....
#1 楼 @watraludru 感谢您的指出,不过这几点我的理解和您不太一致:
Fiber.current
的方法,当require 'fiber'
了之后,它做了以下事情:
```cvoid ruby_Init_Fiber_as_Coroutine(void);
void Init_fiber(void) { ruby_Init_Fiber_as_Coroutine(); }
见ruby-2.1.0源代码下`ext/fiber/fiber.c `
ruby_Init_Fiber_as_Coroutine具体是这么实现的:
```c
void
ruby_Init_Fiber_as_Coroutine(void)
{
rb_define_method(rb_cFiber, "transfer", rb_fiber_m_transfer, -1);
rb_define_method(rb_cFiber, "alive?", rb_fiber_alive_p, 0);
rb_define_singleton_method(rb_cFiber, "current", rb_fiber_s_current, 0);
}
见 ruby-2.1.0 源代码下 cont.c
代码的 1686 行
主 Fiber 这个概念是有的,ruby 中的在某个线程中,如果 require 了 fiber,那么当你在调用其它 fiber 的 resume 方法的时候,当前的线程模式就会转换成 fiber 模式,当前线程的代码堆栈就会成为你当前线程的 root_fiber
VALUE
rb_fiber_current(void)
{
rb_thread_t *th = GET_THREAD();
if (th->fiber == 0) { // 就是这行
/* save root */
rb_fiber_t *fib = root_fiber_alloc(th);
th->root_fiber = th->fiber = fib->cont.self;
}
return th->fiber;
}
见 ruby-2.1.0 源代码下 cont.c
代码的 1333 行
第三个问题: em-synchrony 我通读过所有的代码,但是 fiber 问题不在于 em-synchrony 下简单的实现,而是在于需要定制化你所需求的库的读写,一味将 socket 直接指向 em-synchrony 下的 socket 实现不是一个兼容性非常好的问题
报个名,准备去