EOS区块链究竟有几个线程

一 nodeos工作线程

nodeso节点的工作线程包括:一个主线程,一个信号处理线程和两个线程池。

  1. 主线程:main函数启动线程,该线程执行完程序初始化工作后,会调用app().io_service.run(), 启动boost::asio::io_service的异步io服务,通过异步io方式完成节点块生产,交易处理等主要业务工作。

  2. 信号处理线程:子线程,通过异步io服务,接收系统信号并处理。
  3. 线程池,线程池启动的工作线程数可通过启动参数配置。

    • controller线程池: 异步执行块block_state创建,块中交易验证时的交易解签名计算。
    • 生产者插件线程池:负责异步执行交易解签名计算。

​ 采用默认配置(每个线程池2个工作线程),nodeos节点总线程数是6个,通过pstree命令可查看:
EOS区块链究竟有几个线程

​ nodeos有一个主线程pid=32385,该主线程有5个子线程,32386~32390。

二 主线程

​ main函数执行线程:main函数最后调用app().exec(),启动io_service服务。
EOS区块链究竟有几个线程

app()是application实例,application中定义了io_service对象io_serv:

```c++
class application
{...
std::shared_ptr<boost::asio::io_service> io_serv;
}
application::application():my(new application_impl()){
io_serv = std::make_shared<boost::asio::io_service>();
}

boost::asio::io_service& get_io_service() { return *io_serv; }
//启动io服务
void application::exec() {
io_serv->run();
shutdown();
}

void application::quit() {
my->_is_quiting = true;
io_serv->stop();
}


* 其它插件通过get_io_service()函数,获取io_serv,进行异步io投递。
* 程序退出时,会调用quit()函数,结束io服务。

​        nodeos节点交易处理,出块,块验证等主要业务操作都是在该线程执行的,因为eos中交易不支持并行处理,所以application中的io_serv是不允许在除主线程之外的其它线程中重复执行io_serv.run()操作的。

​        看到application::exec()中的代码,有些人可能会有疑问:asio::io_service监听的io端口都完成(没有待监听的io端口)时,io_serv->run()就会退出。这里io_serv->run()只调用了一次,没有循环调用,不会退出么?

​          nodeos投递到application::io_serv的io处理handle函数,会重复投递该io端口。所以在handle处理函数完成时,io_serv中总会有待完成io存在,io_serv->run()就不会退出,下面以producer_plugin中的_timer为例,看一下这个投递过程:

```c++
producer_plugin::producer_plugin()
   : my(new producer_plugin_impl(app().get_io_service())){
      my->_self = this;
};

class producer_plugin_impl {
  producer_plugin_impl(boost::asio::io_service& io):_timer(io),
  _transaction_ack_channel(app().get_channel<compat::channels::transaction_ack>()){} 
    boost::asio::deadline_timer   _timer;
};

​ producer_plugin()在构造时传入app().get_io_service()构造了producer_plugin_impl:: _timer; 在节点的生产循环函数中可以看到, _timer的handle函数中会调用schedule_production_loop()函数,而在该函数中,又会调用 _timer.asyncwait()重复投递 _timer到ioservice中:
EOS区块链究竟有几个线程

扫描二维码关注公众号,回复: 7126638 查看本文章

三 信号处理线程

EOS区块链究竟有几个线程

  1. 创建io_service对象sig_io_serv;
  2. 将SIGINT信号投递到io_service对象,并绑定信号处理函数,当系统发送SIGINT信号时,会触发该处理函数。
  3. 创建sig_thread信号处理线程,在该线程中调用sig_io_serv->run(),等待信号触发,调用相应处理函数。
  4. 信号处理线程只处理SIGINT,SIGTERM,SIGPIPE这三个系统信号,一旦收到信号,会调用退出操作,使nodeos退出。

四 controller线程池

4.1 定义及创建

```c++
struct controller_impl {
...
boost::asio::thread_pool thread_pool;
}

controller_impl( const controller::config& cfg, controller& s ):self(s),
chain_id( cfg.genesis.compute_chain_id() ),
read_mode( cfg.read_mode ),
...
thread_pool( cfg.thread_pool_size )
{...}


 * controller线程池定义在controller_impl中;
 * 根据配置参数中设定的thread_pool_size创建相应数量的工作线程。

#### 4.2 异步任务

​        执行块相关操作时,较耗时且与排序无关的动作都会投递到controller线程池执行。eos目前投递到该线程池执行的操作有两个:

​                    1 块交易验证时的解签名操作;

​                   2 块状态block_state数据创建操作(两轮共识计算都在这里完成)。

##### 4.2.1 解签名操作

​      节点收到块,会调用apply_block()函数执行块中交易,进行块验证。期间会投递交易解签名计算到controller线程池:
![](https://s1.51cto.com/images/blog/201908/29/abd10cc2be7e1775ac39cb82f431c320.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

​         解签名投递函数create_signing_keys_futrue():
![](https://s1.51cto.com/images/blog/201908/29/55b1d48be5f4c5964090084828a7baa2.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

* 在线程池thread_pool中异步执行解签名函数,异步解签名的执行结果,会放入交易的signing_keys_future中。
* 真正解签名算法是trn.get_signature_keys(),解出的公钥放到recovered_pub_keys中。

##### 4.2.2 block_state创建
![](https://s1.51cto.com/images/blog/201908/29/4ffb6121ddd0ed6cffd1adf8e9081fa1.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)

### 五 生产者线程池

#### 5.1  定义及创建

​      生产者线程池定义在producer_plugin_impl中,执行插件初始化函数plugin_initialize()时会根据配置参数创建工作线程。

```c++
class producer_plugin_impl : public std::enable_shared_from_this<producer_plugin_impl> {
   public:
      void schedule_production_loop();//生产处理循环
     //线程池定义
     fc::optional<boost::asio::thread_pool>   _thread_pool;//异步线程池
    ...
}

void producer_plugin::plugin_initialize(const boost::program_options::variables_map& options)
{ 
  auto thread_pool_size = options.at( "producer-threads" ).as<uint16_t>();
  //线程池创建 
  my->_thread_pool.emplace( thread_pool_size );
}

5.2 异步任务

​ 生产者线程池负责的工作任务有两个:

       1. 为接收到的投递交易进行异步解签名计算;
          2. 等待解签名计算完成,将交易投递到主线程的异步io服务中处理。

​ 这两个工作任务都在同一个函数中投递执行:
EOS区块链究竟有几个线程

该函数在节点收到其它节点/客户端投递的交易时被调用:

  1. 投递异步解签名计算任务到 _threadpool线程池( 生产者线程池),计算结果放到

    trx->signing_keys_future中。

  2. 投递异步任务到 _thread_pool线程池,该任务等待将异步解签名计算结束的交易投递到主线程的io_service中执行。

5.3 线程池关闭

​ 当调用插件shutdown函数时,会执行线程池关闭动作,前程池中的工作线程退出。
EOS区块链究竟有几个线程

六 链接

星河公链

猜你喜欢

转载自blog.51cto.com/14267585/2433560
今日推荐