• 服务器开发


        

    在高性能的网络程序中,使用得最为广泛的恐怕要数 “non-blocking IO + IO multiplexing”这种模型,即 Reactor 模式

    while (!done)
    {
    int timeout_ms = max(1000, getNextTimedCallback());
    int retval = ::poll(fds, nfds, timeout_ms);
    if (retval < 0) {
    处理错误
    } else {
    处理到期的 timers
    if (retval > 0) {
    处理 IO 事件
    }
    }
    }

    =================================================================

    1.每个请求创建一个线程,使用阻塞式 IO 操作。在 Java 1.4 引入 NIO 之前,这是Java 网络编程的推荐做法。可惜伸缩性不佳。

    2. 使用线程池,同样使用阻塞式 IO 操作。与 1 相比,这是提高性能的措施。

    3. 使用 non-blocking IO + one loop per thread。即 Java NIO 的方式。
    4. Leader/Follower 等高级模式

    One loop per thread
    此种模型下,程序里的每个 IO 线程有一个 event loop (或者叫 Reactor),用 于 处 理
    读写和定时事件(无论周期性的还是单次的),代码框架跟第 2 节一样。
    这种方式的好处是:
    � 线程数目基本固定,可以在程序启动的时候设置,不会频繁创建与销毁。
    � 可以很方便地在线程间调配负载。
    event loop 代表了线程的主循环,需要让哪个线程干活,就把 timer 或 IO channel
    (TCP connection) 注册到那个线程的 loop 里即可。对实时性有要求的 connection 可
    以单独用一个线程;数据量大的 connection 可以独占一个线程,并把数据处理任务分
    摊到另几个线程中;其他次要的辅助性 connections 可以共享一个线程。
    对于 non-trivial 的服务端程序,一般会采用 non-blocking IO + IO multiplexing,每个
    connection/acceptor 都会注册到某个 Reactor 上,程序里有多个 Reactor,每个线程至多
    有一个 Reactor。
    多线程程序对 Reactor 提出了更高的要求,那就是“线程安全”。要允许一个线程往别
    的线程的 loop 里塞东西,这个 loop 必须得是线程安全的。
    线程池
    不过,对于没有 IO 光有计算任务的线程,使用 event loop 有点浪费,我会用有一种
    补充方案,即用 blocking queue 实现的任务队列(TaskQueue):
    blocking_queue<boost::function<void()> > taskQueue;
    void worker_thread()
    {
    while (!quit) {
    boost::function<void()> task = taskQueue.take();
    task(); // 在产品代码中需要考虑异常处理
    }
    }
    用这种方式实现线程池特别容易:
    启动容量为 N 的线程池:
    int N = num_of_computing_threads;
    for (int i = 0; i < N; ++i) {
    create_thread(&worker_thread); // 伪代码:启动线程
    }
    4
    // 线程安全的阻塞队列
    // this blocks使用起来也很简单:
    boost::function<void()> task = boost::bind(&Foo::calc, this);
    taskQueue.post(task);
    上面十几行代码就实现了一个简单的固定数目的线程池,功能大概相当于 Java 5 的
    ThreadPoolExecutor 的某种“配置”。当然,在真实的项目中,这些代码都应该封装到一个
    class 中,而不是使用全局对象。另外需要注意一点:Foo 对象的生命期,我的另一篇博客
    《当析构函数遇到多线程——C++ 中线程安全的对象回调》详细讨论了这个问题
    http://blog.csdn.net/Solstice/archive/2010/01/22/5238671.aspx
    除了任务队列,还可以用 blocking_queue<T> 实现数据的消费者-生产者队列,即 T 的
    是数据类型而非函数对象, queue 的消费者(s)从中拿到数据进行处理。这样做比 task queue
    更加 specific 一些。
    blocking_queue<T> 是多线程编程的利器,它的实现可参照 Java 5 util.concurrent 里的
    (Array|Linked)BlockingQueue,通常 C++ 可以用 deque 来做底层的容器。Java 5 里的代
    码可读性很高,代码的基本结构和教科书一致(1 个 mutex,2 个 condition variables),
    健壮性要高得多。如果不想自己实现,用现成的库更好。(我没有用过免费的库,这里就不
    乱推荐了,有兴趣的同学可以试试
    Intel Threading Building Blocks 里 的
    concurrent_queue<T>。)
    归纳
    总结起来,我推荐的多线程服务端编程模式为:event loop per thread + thread pool。
    � event loop 用作 non-blocking IO 和定时器。
    � thread pool 用来做计算,具体可以是任务队列或消费者-生产者队列。

    =========================================================================

     eventloop 用作 non-blockingIO 和定时器。

     threadpool 用来做计算,具体可以是任务队列或消费者-生产者队列

    任务对列,生产消费者 线程池

    TaskQueue、Producer-Consumer Queue、 CountDownLatch 

    PTHREAD_MUTEX_ERRORCHECK排错

     

    条件变量是非常底层的同步原语, 很少直接使用, 一般都是用它来实现高层的同施步,措 如 BlockingQueue 或 CountDownLatch

     

    线程 分析工具如 Intel Thread Checker 和 Valgrind-Helgrind 等能识别 pthreads 调用,并依据 happens-before 关系 [Lamport 1978] 分析程序有无 data race

    使用互斥器的条件变量的惯用手法 (idiom),关键是 RAII

     

     同步线程处理业务逻辑,异步线程处理IO

    异步线程只有一个,由主线程当

    每个线程开启一个事件loop ,及epoll_wait             

    microhttp, libpg, libdrizzle, quickfix

    https://google-glog.googlecode.com/files/glog-0.3.3.tar.gz

    blocking_queue<T> 是多线程编程的利器,它的实现可参照 Java 5 util.concurrent 里的
    (Array|Linked)BlockingQueue,通常 C++ 可以用 deque 来做底层的容器

  • 相关阅读:
    10:简单密码
    08:字符替换
    07:配对碱基链
    05:输出亲朋字符串
    18:等差数列末项计算
    09:密码翻译
    用最通俗的话说23种设计模式之代理模式
    Android学习之 UI效果
    精确到时分秒的jQuery插件例子
    Eclipse 常用快捷键
  • 原文地址:https://www.cnblogs.com/anjsxz/p/3714545.html
Copyright © 2020-2023  润新知