web 项目–并发之[半同步/半分应堆]

并发编程模式

并发编程方法的实现有多线程和多进程两种,但这里涉及的并发模式指 I/O 处理单元与逻辑单元的协同完成任务的方法。

  • 半同步/半异步模式
  • 领导者/追随者模式

半同步/半反应堆

半同步/半反应堆并发模式是半同步/半异步的变体,将半异步具体化为某种事件处理模式.

并发模式中的同步和异步

  • 同步

    程序完全按照代码序列的顺序执行

  • 异步

    程序的执行需要由系统事件驱动

半同步/半异步模式工作流程

  • 同步线程用于处理客户逻辑
  • 异步线程用于处理 I/O 事件
  • 异步线程监听到客户请求后,就将其封装成请求对象并插入请求队列中
  • 请求队列将通知某个工作在同步模式的工作线程来读取并处理该请求对象

半同步/半反应堆工作流程(以 Proactor 模式为例)

  • 主线程充当异步线程,负责监听所有 socket 上的事件
  • 若有新请求到来,主线程接收以得到新的连接 socket,然后往 epoll 内核事件表中注册该 socket 上的读写事件
  • 如果连接 socket 上有读写事件发生,主线程从 socket 上接收数据,并将数据封装成请求对象插入到请求队列中
  • 所有工作线程睡眠在请求队列上,当有任务到来时,通过竞争(如互斥锁)获得任务的接管权

基础知识

静态成员变量

将类成员变量声明为 static,则为静态成员变量,与一般的成员变量不同,无论建立多少对象,都只有一个静态成员变量的拷贝,静态成员变量属于一个类,所有对象共享。

静态变量在编译阶段就分配了空间,对象还没创建时就已经分配了空间,放到全局静态区。

  • 静态成员变量

    • **最好是类内声明,类外初始化(以免类名访问静态成员访问不到 ) **

      • 类外初始化的原因是 :==静态成员变量是和类绑定, 而不是和具体的类成员绑定==

        ​ 如果在类内初始化, 会导致每个类都会对该静态成员初始化, 导致静态成员和具体的类成员绑定(即每个对象都含有该静态成员), 这与静态成员的定义是矛盾的, 所以应该在类外进行初始化

    • 无论公有,私有,静态成员都可以在类外定义,但私有成员仍有访问权限。

      • 若它为 public 类型,当某个对象修改了它,也会影响到其他对象,所有对象都是“同甘共苦的”
    • 非静态成员类外不能初始化。

      • 通常把静态成员放在类的实现文件中初始化

        ​ c++规定 const 静态类成员可以直接初始化,其他==非 const 的静态类成员需要在类外初始化,且不能使用 static 关键字==

      C++ Primer 关于静态成员举了个例子:

      ​ 一个银行账户类需要一个数据成员来表示当前的利率。在这个类中,我们所希望的是利率与类相关联,而不是与类的每个对象相关联。这个时候就可以用静态成员函数,可以提高效率,而且一旦利率浮动,每个对象也能相应使用新的值。

    • 静态成员数据是共享的。

      • 静态变量通过类来调用,也可以通过具体对象来调用,但非静态变量只能通过具体对象来调用

        ​ 静态成员的声明要加个关键 static。==静态成员可以通过双冒号来使用即<类名>::<静态成员名>,非静态成员则不能==


静态成员函数

将类成员函数声明为 static,则为静态成员函数。

  • 静态成员函数可以直接访问静态成员变量,不能直接访问普通成员变量,但可以通过参数传递的方式访问。

  • 普通成员函数可以访问普通成员变量,也可以访问静态成员变量。

  • 静态成员函数没有 this 指针。

    ==非静态数据成员为对象单独维护,但静态成员函数为共享函数,无法区分是哪个对象==,因此不能直接访问普通变量成员,也没有 this 指针。


pthread_create 陷阱

首先看一下该函数的函数原型。

1
2
3
4
5
1#include <pthread.h>
2int pthread_create (pthread_t *thread_tid, //返回新生成的线程的id
3 const pthread_attr_t *attr, //指向线程属性的指针,通常设置为NULL
4 void * (*start_routine) (void *), //处理线程函数的地址
5 void *arg); //start_routine()中的参数

函数原型中的第三个参数,为函数指针,指向处理线程函数的地址。该函数,要求为静态函数。如果处理线程函数为类成员函数时,需要将其设置为静态成员函数


this 指针的锅

pthread_create 的函数原型中第三个参数的类型为函数指针,指向的线程处理函数参数类型为(void *),若线程函数为类成员函数,则 this 指针会作为默认的参数被传进函数中,从而和线程函数参数(void*)不能匹配,不能通过编译。

静态成员函数就没有这个问题,里面没有 this 指针。