前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >nodejs创建线程问题

nodejs创建线程问题

作者头像
theanarkh
发布2020-08-14 11:13:51
9260
发布2020-08-14 11:13:51
举报
文章被收录于专栏:原创分享原创分享

我们知道在nodejs中可以使用new Worker创建线程。今天有个同学恰好问到,怎么判断创建线程成功,这也是最近开发线程池的时候遇到的问题。nodejs文档里也没有提到如何捕获创建失败这种情况。所以只能通过源码去找答案。不过坏消息是,我们无法捕获这个这个错误。下面看一下源码。我们直接从c++层开始分析。 当我们调用new Worker的时候,最后会调用c++的StartThread函数(node_worker.cc)创建一个线程。

代码语言:javascript
复制
CHECK_EQ(uv_thread_create_ex(&w->tid_, &thread_options, [](void* arg) {
    // ...
  }, static_cast<void*>(w)), 0);

我们看uv_thread_create_ex的逻辑

代码语言:javascript
复制
int uv_thread_create_ex(uv_thread_t* tid,
                        const uv_thread_options_t* params,
                        void (*entry)(void *arg),
                        void *arg) {
  // 忽略部分代码
  err = pthread_create(tid, attr, f.out, arg);
  return UV__ERR(err);
}

接着我们看一下pthread_create的返回值定义

On success, pthread_create() returns 0; on error, it returns an error number, and the contents of *thread are undefined.

所以,如果uv_thread_create_ex返回非0,即pthread_create返回非0。表示报错。我们回头看一下返回非0时,c++的处理。我们对c++层的CHECK_EQ(uv_thread_create_ex(…), 0)进行宏展开。

代码语言:javascript
复制
#define CHECK_EQ(a, b) CHECK((a) == (b))

#define CHECK(expr)                                                           \
  do {                                                                        \
    if (UNLIKELY(!(expr))) {                                                  \
      ERROR_AND_ABORT(expr);                                                  \
    }                                                                         \
  } while (0)

#define UNLIKELY(expr) expr

通过一些列展开,最后变成

代码语言:javascript
复制
  do {                                                                        
    if (!(返回值 == 0)) {                                                  
      ERROR_AND_ABORT(expr);                                                  
    }                                                                         
  } while (0)

因为创建线程时返回非0,所以这里是true。我们继续看ERROR_AND_ABORT

代码语言:javascript
复制
#define ERROR_AND_ABORT(expr)                                                 \
  do {                                                                
    static const node::AssertionInfo args = {                                 \
      __FILE__ ":" STRINGIFY(__LINE__), #expr, PRETTY_FUNCTION_NAME           \
    };                                                                        \
    node::Assert(args);                                                       \
  } while (0)

拼接错误信息,然后执行node::Assert(args);

代码语言:javascript
复制
[[noreturn]] void Assert(const AssertionInfo& info) {
  char name[1024];
  GetHumanReadableProcessName(&name);

  fprintf(stderr,
          "%s: %s:%s%s Assertion `%s' failed.\n",
          name,
          info.file_line,
          info.function,
          *info.function ? ":" : "",
          info.message);
  fflush(stderr);

  Abort();
}

重点是Abort,

代码语言:javascript
复制
[[noreturn]] void Abort() {
  DumpBacktrace(stderr);
  fflush(stderr);
  ABORT_NO_BACKTRACE();
}

继续看ABORT_NO_BACKTRACE

代码语言:javascript
复制
#ifdef _WIN32
#define ABORT_NO_BACKTRACE() _exit(134)
#else
#define ABORT_NO_BACKTRACE() abort()
#endif

所以最终调用的是_exit或abort退出或者终止进程。我们讨论linux下的情况。我们看abort函数的说明

The abort() function first unblocks the SIGABRT signal, and then raises that signal for the calling process (as though raise(3) was called). This results in the abnormal termination of the process unless the SIGABRT signal is caught and the signal handler does not return (see longjmp(3)). If the SIGABRT signal is ignored, or caught by a handler that returns, the abort() function will still terminate the process. It does this by restoring the default disposition for SIGABRT and then raising the signal for a second time.

abort函数会给进程发送SIGABRT信号,我们可以注册函数处理这个信号,不过我们还是无法阻止进程的退出,因为他执行完我们的处理函数后,会把处理函数注册为系统的默认的,然后再次发送SIGABRT信号,而默认的行为就是终止进程。我们来个测试。

代码语言:javascript
复制
const { Worker, threadId } = require('worker_threads');
for (let i = 0; i < 1000; i++) {
    const worker = new Worker('var a = 1;', { eval: true });
}

我们创建1000个线程。结果

总结:在nodejs创建过多的线程可能会导致进程终止。而我们无法阻止这个行为。所以在nodejs里使用多线程的时候,我们需要注意的就是不要开启过多的线程,而在创建线程的时候,我们也不需要关注是否成功,因为只要进程不挂掉,那就是成功。对于业务错误我们可以注册error事件处理,在new Worker的时候,我们可以加try catch。可以捕获一下参数错误的情况。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档