libuv里的几个缺陷

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                       

libuv是node.js使用的基础库,主要包括主循环,文件和网络接口。虽然libuv是为node.js而生的,但它本身是一个独立的库,加上使用简单方便,所以在node.js之外也有不少人使用。最近整合libuv到V8里时发现几个问题:

1.uv_fs相关函数无法传回调函数需要的上下文信息(如read的buffer),只能通过全局变量来保存数据(官方例子都是用的全局变量)。uv_fs相关函数都可以提供一个回调函数,如果回调函数不为空,当前调用自动变成一个异步调用,在操作完成时调用提供的回调函数。一般来说,回调函数都需要一个变量来作为它的上下文(提供参数或保存结果),文件操作相关函数的上下文是uv_fs_t结构,但里面无法保存调用者提供的额外信息。

uv_fs_t open_req;uv_fs_t read_req;uv_fs_t write_req;static char buffer[1024];static uv_buf_t iov;...void on_read(uv_fs_t *req) {    if (req->result < 0) {        fprintf(stderr, "Read error: %s\n", uv_strerror(req->result));    }    else if (req->result == 0) {        uv_fs_t close_req;        // synchronous        uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);    }    else if (req->result > 0) {        iov.len = req->result;        uv_fs_write(uv_default_loop(), &write_req, 1, &iov, 1, -1, on_write);    }}void on_open(uv_fs_t *req) {    // The request passed to the callback is the same as the one the call setup    // function was passed.    assert(req == &open_req);    if (req->result >= 0) {        iov = uv_buf_init(buffer, sizeof(buffer));        uv_fs_read(uv_default_loop(), &read_req, req->result,                   &iov, 1, -1, on_read);    }    else {        fprintf(stderr, "error opening file: %s\n", uv_strerror((int)req->result));    }}...uv_fs_open(uv_default_loop(), &open_req, argv[1], O_RDONLY, 0, on_open);
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

我开始以为data成员是保存用户数据的地方,通过data把buffer传过去,但是data总是被清空了,看里面的代码才知道data是fs内部的使用的。上面官方例子都是通过全局变量传过去的,真是太变态了!

2.其它线程不能访问缺省主循环。最近让cantk-runtime-v8里支持Touch/Key事件遇到这个问题:Touch/Key事件是UI线程里的,而V8是在GLSurfaceView的Render线程里的,直接通过JNI调用V8里的JS会导致程序崩溃,所以我想通过uv_idle来串行化,但结果是程序仍然崩溃。记得glib loop里idle是允许多线程访问的,我在设计FTK的主循环时也通过一个pipe来串行化多线程访问主循环的,呵呵,我以为所有的loop都应该支持多线程,一看代码才知道,它并没有加锁也没有用pipe来串行化:

  int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) {           \    if (uv__is_active(handle)) return 0;                                      \    if (cb == NULL) return -EINVAL;                                           \    QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue);         \    handle->name##_cb = cb;                                                   \    uv__handle_start(handle);                                                 \    return 0;                                                                 \  }    
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3.uv_async无法传递数据。用uv_idle不行,我决定用uv_async。这次倒是不崩溃了,事件好像也收到了,但游戏里的反应却有些怪异,仔细分析LOG信息,发现touchmove和touchend收到了,但是没有收到touchstart。明明uv_async_send都执行了,为什么主循环却没有处理这个事件呢?继续看代码:

void uv__async_send(struct uv__async* wa) {  const void* buf;  ssize_t len;  int fd;  int r;  buf = "";  len = 1;  fd = wa->wfd;#if defined(__linux__)  if (fd == -1) {    static const uint64_t val = 1;    buf = &val;    len = sizeof(val);    fd = wa->io_watcher.fd;  /* eventfd */  }#endif  do    r = write(fd, buf, len);  while (r == -1 && errno == EINTR);  if (r == len)    returnif (r == -1)    if (errno == EAGAIN || errno == EWOULDBLOCK)      return;  abort();}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

async_send是同步执行的,从上面的代码看不出什么问题,再看接受部分:

static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {  struct uv__async* wa;  char buf[1024];  unsigned n;  ssize_t r;  n = 0for (;;) {    r = read(w->fd, buf, sizeof(buf));    if (r > 0)      n += r;    if (r == sizeof(buf))      continue;    if (r != -1)      break;    if (errno == EAGAIN || errno == EWOULDBLOCK)      break;    if (errno == EINTR)      continue;    abort();  }  wa = container_of(w, struct uv__async, io_watcher);#if defined(__linux__)  if (wa->wfd == -1) {    uint64_t val;    assert(n == sizeof(val));    memcpy(&val, buf, sizeof(val));  /* Avoid alignment issues. */    wa->cb(loop, wa, val);    return;  }#endif  wa->cb(loop, wa, n);}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

全部数据读取完了,只是在最后调了一次回调。更郁闷的是我对async的理解是错的:uv_async_t里的data成员并不能用来传递数据,它是在两个线程中无保护的情况下共享的。下面的代码是官方提供的示例,这种方法在低速通信时没问题,速度一快后面的数据自动覆盖前面的数据,所以touchstart被touchmove覆盖而丢失。

扫描二维码关注公众号,回复: 3927634 查看本文章
void fake_download(uv_work_t *req) {    int size = *((int*) req->data);    int downloaded = 0;    double percentage;    while (downloaded < size) {        percentage = downloaded*100.0/size;        async.data = (void*) &percentage;        uv_async_send(&async);        sleep(1);        downloaded += (200+random())%1000; // can only download max 1000bytes/sec,                                           // but at least a 200;    }}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

libuv以上这些问题在node.js恰恰不是问题,但独立使用libuv时一定要小心了。

           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

这里写图片描述

猜你喜欢

转载自blog.csdn.net/jggyff/article/details/83691336