文章目录

  • 简介
  • 代码示例
  • 参考

简介

很多时候,我们的可调用对象需要某些非参数依赖的执行环境。可以这么理解,我们的任务是就绪的,不需要依赖外界传入参数了,只需要以某种方式控制方法的whenwherehow即可。
三个分别是指:

  • where:可调用对象的执行位置,比如可以在内部或者外部的处理器(线程)上执行,并从内部或者外部的处理器(线程)上获取结果
  • when:可调用对象的执行时间,比如立刻执行或者被调度执行
  • how:可调用对象的执行方式,比如在CPU或者GPU上执行,可以矢量化计算等

举个例子:

int foo(int i) {return i * 2;
}// ...
auto f = std::async(std::launch::async, foo(10));
f.get();
// ...

上述代码中,对于foo(10)来说,关键要素是:

  • when:调用std::async的时候
  • where:在std::async内部启动的新线程中
  • how:在CPU上执行,正常顺序执行

很多时候,用户想要获取一个通用的接口类,这个类提供了统一的接口,而且规定了上述的3w,之后只需要把可执行对象传入接口类中,就能根据该接口类的3w,最终获取结果。

那么,我们可以把上面说的接口类,认为是一个Executor。甚至实际的Executor甚至仅仅是个接口,仅提供了统一的调用方法,让用户自己去实现有关实际功能等。

因此,可以这么理解,Executor给可调用的对象提供了一个运行环境或者说是运行上下文环境 Execute Context。个人理解,运行上下文是区别于平时说的上下文 Context。因为Context是针对编程环境来说的,或者认为是Context决定了可调用对象的运行形式,能改变可调用对象的表现;而Execute Context无法改变上面的,其只能改变我们说的3w,而3w不决定可调用对象的结果。

比如Executor可以是不同类型的线程池,比如一个线程一个队列或者多线程共享队列等。只要指定了Executor,用户就可以传入可调用对象,并等待结果;它屏蔽了底层的执行细节。

C++目前还不支持Executor的模式,或者说支持的比较弱。。比如std::async中,无法指定Executor,只能以std::launch::async启动新的线程或者std::launch::defered延后执行等。下面以代码示例,简单演示下支持Executorasync_exec函数。

代码示例

以一个多线程共享队列的线程池:

#include <thread>
#include <iostream>
#include <functional>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <deque>
#include <random>
#include <atomic>// Executor,仅仅提供接口
class Executor {public:Executor() = default;virtual ~Executor() = default;virtual void add(std::function<void()> fn) = 0;
};// 实际的线程池,Executor的具体表现,可以忽略这部分的具体实现
class ThreadPool : public Executor {public:explicit ThreadPool(size_t threadCnt = 4) : m_threadCnt(4), m_stop(false) {std::thread([this]() { run(); }).detach(); // 后台启动}~ThreadPool() override {m_stop = true;m_cond.notify_all();for (auto &t: m_threads) {if (t.joinable()) {t.join();}}}ThreadPool(ThreadPool &) = delete;ThreadPool(ThreadPool &&) = delete;void add(std::function<void()> fn) override {m_mtx.lock();m_tasks.emplace_back(std::move(fn));m_mtx.unlock();m_cond.notify_one();}private:void run() {for (size_t i = 0; i < m_threadCnt; ++i) {m_threads.emplace_back(std::thread([this]() {for (;;) {std::unique_lock<std::mutex> lck(m_mtx);m_cond.wait(lck, [this]() -> bool { return m_stop || !m_tasks.empty(); });if (m_stop) {return;}auto &fn = m_tasks.front();m_tasks.pop_front();lck.unlock(); // 一定要释放掉锁,否则无法并发fn();}}));}}private:std::vector<std::thread> m_threads;std::deque<std::function<void()>> m_tasks;std::condition_variable m_cond;std::mutex m_mtx;std::atomic<bool> m_stop{false};size_t m_threadCnt;
};// 这里泛型提供了使用Exec的例子
template<typename Fn, typename Exec = Executor>
void async_exec(Fn fn, Exec &exec) {exec.add(fn);
}// 工具函数,获取随机数
inline int get_random(int low, int high) {std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<> distrib(low, high);return distrib(gen);
}void foo(int n) {int t = get_random(100, 5000);std::this_thread::sleep_for(std::chrono::milliseconds(t)); // 模拟执行任务std::cout << "foo finish task " << n << " with " << t << " ms\n";
}int main() {ThreadPool tp(4);for (int i = 0; i < 10; ++i) {async_exec([i]() { foo(i); }, tp); // 指定实际的Executor执行}std::this_thread::sleep_for(std::chrono::seconds(15));  // 休眠15s等所有任务完成std::cout << "stop everything\n";return 0;
}

参考

  • https://github.com/facebook/folly/blob/master/folly/docs/Executors.md
  • https://www.modernescpp.com/index.php/a-short-detour-executors
  • http://www.vollmann.ch/en/presentations/executors2018.pdf
  • https://stackoverflow.com/questions/42177803/what-is-the-executor-pattern-in-a-c-context
  • https://www.linuxtopia.org/online_books/programming_books/c++_practical_programming/c++_practical_programming_267.html

理解C++ Executor的设计理念相关推荐

  1. MyBatis源码-深入理解MyBatis Executor的设计思想

    文章目录 Pre JDBC的执行过程 JDBC Demo JDBC Statement 接口 MyBatis执行过程 四大组件 组件之间的关系 Executor 执行器组件 架构总览 接口继承关系 P ...

  2. spark学习-42-Spark的driver理解和executor理解

    1.看了很多网上的图,大多是dirver和executor之间的图,都不涉及物理机器 如下图,本人觉得这些始终有些抽象 看到这样的图,我很想知道driver program在哪里啊,鬼知道?为此我自己 ...

  3. 线程中这么调用类_「手撕面试官」谈谈你对JDK中Executor的理解?

    欢迎关注头条号:Java小野猫 前言 随着当今处理器计算能力愈发强大,可用的核心数量越来越多,各个应用对其实现更高吞吐量的需求的不断增长,多线程 API 变得非常流行.在此背景下,Java自JDK1. ...

  4. MyBatis源码-解读Executor的三个实现类之BatchExecutor(批处理执行器)

    文章目录 Pre Executor 执行器 接口继承关系 BatchExecutor(重用执行器) 入门小demo 源码 BatchExecutor VS ReuseExecutor Pre MyBa ...

  5. MyBatis源码-解读Executor的三个实现类之SimpleExecutor(简单执行器)

    文章目录 Pre Executor 执行器 接口继承关系 SimpleExecutor(简单执行器) 入门小demo 实例化SimpleExecutor doQuery方法 Pre MyBatis源码 ...

  6. Executor 与 ExecutorService 和 Executors 傻傻分不清

    转载自  Executor 与 ExecutorService 和 Executors 傻傻分不清 java.util.concurrent.Executor, java.util.concurren ...

  7. 【译】Executor, ExecutorService 和 Executors 间的不同

    Executor, ExecutorService 和 Executors 间的不同 java.util.concurrent.Executor, java.util.concurrent.Execu ...

  8. Unix 是简单的,你不需要成为一个天才或是计算机专家也能理解它!

    载自 : <完全用Linux工作> 完全用Linux工作,抛弃windows 我已经半年没有使用 Windows 的方式工作了.Linux 高效的完成了我所有的工作. GNU/Linux ...

  9. Executor, ExecutorService 和 Executors 间的不同

    java.util.concurrent.Executor, java.util.concurrent.ExecutorService, java.util.concurrent. Executors ...

最新文章

  1. JavaWEB后端支付银联,支付宝,微信对接
  2. RealSense开发-Session和SenseManager的几种创建方法
  3. C代码+汇编 C的for汇编学习分析
  4. 【计算机学科】最好的学校排名
  5. 记录遇到的Python陷阱和注意点
  6. [HTTP] Cookie
  7. Python 机器学习在线指南
  8. php 数组作用域,如何在php中访问私有作用域命名空间数组数据?
  9. 基于Python+Django+Mysql的图书管理系统
  10. Clojure 学习入门(6)- 函数定义
  11. java 输出二进制文件_Java输出小端二进制文件
  12. ffmpeg的一些用法,不定期更新
  13. 像素深度、分辨率与图片大小的关系?图片大小怎么计算?
  14. 【推荐系统论文精读系列】(八)--Deep Crossing:Web-Scale Modeling without Manually Crafted Combinatorial Features
  15. “为了交项目干杯”对“那周余嘉熊掌将得队”、“男上加男,强人所男”的Beta产品测试报告...
  16. 用cookie登录KinhDown教程
  17. xmap 配置php环境,xmap 的动态 - SegmentFault 思否
  18. Microsoft Outlook 2019 for mac(电子邮件和日历工具)
  19. 浏览器全屏和pc显示器全屏
  20. 几个超好的Spring boot实战项目 (还不赶紧收藏起来)

热门文章

  1. 计算机算法设计与分析 数字三角形
  2. AcWing479.加分二叉树(区间DP)题解
  3. Android SDK Manager配置
  4. 【less-2】sqli-labs靶场第二关
  5. 【干货】TCP/IP协议三次握手四次挥手
  6. 机器学习笔记网盘分享
  7. 编译程序 解释程序
  8. 数据库的高级SQL特性
  9. 翻转子串(important!)
  10. stm的小型开源gui介绍