Linux多线程服务端编程(笔记1)

          shared_ptr以const reference方式作为函数参数传递。只要有指向对象x的shared_ptr存在对象x就不会析构,当指向x的最后一个shared_ptr析构或reset时x会被销毁.weak_ptr不控制对象生命期,但是它知道对象是否活着:若对象还活着它可以提升为shared_ptr,否则返回一个空shared_ptr,提升行为是线程安全的.

          boost::bind将类似函数的东西和参数绑定在一个boost::function上,例如:不实例化对象就可以使用对象的成员函数。bind会将实参拷贝一份,若参数是个shared_ptr则对象的生命期将不会短于boost::function对象。对于非静态成员函数则 bind 要有 n+2 个参数: 指向成员函数fun的指针, 绑定到this的对象, n个参数.如:boost::bind(&A::func, a, 3, 4)()

shared_ptr拷贝开销比原始指针高,一个线程在最外层函数有一个实体shared_ptr对象,之后都用const reference来使用这个shared_ptr(shared_ptr是栈上对象不可能被别的对象看见所以是线程安全的)

          shared_ptr订制析构功能(在创建对象时捕获释放动作):shared_ptr<Y *p,D d>传入一个函数指针或仿函数d,在析构对象时执行d(ptr),ptr是shared_ptr保存的对象指针。在这里删除器d可以是一个函数对象,也可以是一个函数指针,只要它能够像函数那样被调用,使得d(p)成立即可。对删除器的要求是它必须是可拷贝的,行为必须也像delete那样,不能抛出异常。shared_ptr<FILE> fp(fopen("1.txt","r"),fclose)

          scoped_ptr:它能够保证在离开作用域后对象被自动释放,boost::scoped_ptr的实现和std::auto_ptr非常类似,都是利用了一个栈上的对象去管理一个堆上的对象,从而使得堆上的对象随着栈上的对象销毁时自动删除。不同的是,boost::scoped_ptr有着更严格的使用限制——不能拷贝。这就意味着:boost::scoped_ptr指针是不能转换其所有权的。不能转换所有权boost::scoped_ptr所管理的对象生命周期仅仅局限于一个区间(该指针所在的"{}"之间),无法传到区间之外,这就意味着boost::scoped_ptr对象是不能作为函数的返回值的(std::auto_ptr可以)。不能共享所有权这点和std::auto_ptr类似。这个特点一方面使得该指针简单易用。另一方面也造成了功能的薄弱——不能用于stl的容器中。不能用于管理数组对象由于boost::scoped_ptr是通过delete来删除所管理对象的,而数组对象必须通过deletep[]来删除,因此boost::scoped_ptr是不能管理数组对象的,如果要管理数组对象需要使用boost::scoped_array类。

          弱回调:如果对象还活着就调用它的成员函数否则忽略。利用weak_ptr在回调的时候尝试提升为shared_ptr,若提升成功说明对象还健在,执行回调,否则不回调

          回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。回调,就是客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数。例如Win32下的窗口过程函数就是一个典型的回调函数。

            一般说来,C不会自己调用B,C提供B的目的就是让S来调用它,而且是C不得不提供。由于S并不知道C提供的B叫甚名谁,所以S会约定B的接口规范(函数原型),然后由C提前通过S的一个函数R告诉S自己将要使用B函数,这个过程称为回调函数的注册,R称为注册函数。

          delegate:以前的函数指针只是一个单纯的指针不包含回调函数的任何信息(参数个数类型返回值等),这种方式不是类型安全的。委托则是类型安全的,它本身是一个类,一类具有相同签名的函数。

          同步原语:send时阻塞直至发送完成,receive时阻塞直至接收完成

          可重入:多次执行过程相同,可以中断。而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。

          若一个私有继承boost::noncopyable,则该类不能被赋值(复制),boost/utility

          mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中

          一个线程安全的类在动态创建并通过指针访问对象时,多个线程(通过简单的拷贝指针)可能操作同一对象,诸如析构安全之类的问题随之而来

          不要在构造期间泄露this指针:不要在构造函数中注册任何回调,this传给跨线程对象。采用二段式构造:构造函数+initilize()

          class的数据成员mutex只能用于同步本class的其它数据成员(实质是保护成员函数临界区的互斥器有效),但是析构函数会将mutex销毁从而mutex不能保护安全的析构。

          动态对象是否还活着,光看指针是看不出来的,访问无效指针只会出错

          对象关系:组合-对象间有属于关系在多线程不会有问题,关联-a通过b的指针或引用调用b的成员函数但是b的生命期不由a控制,聚合-在关联基础上多了整体和局部的关系,后两种涉及生命期可能出现竞态问题

          采用对象池解决生命期问题:申请对象在对象池查找若有则直接拿来用,否则新建用完后放到对象池。这个方案会引起很多问题:整个对象池的竞态,对象放回时的竞态(半放回),对象类型的多变,内存泄露与分片。

          若对象x注册了任何非静态成员函数的回调(如x向服务端注册,服务端可能在某一事件或时机发生时调用x的成员函数),那么必然在某处持有了x的指针,这就可能出现了竞态

          Observer模式:订阅者发布者模式-Observer订阅Observable的文章,则Observer则需要向Observable注册,然后Observable有新文章时调用x的成员函数(注册函数回调)。其中发布者和订阅者之间如何得知对方是否还存在。

          Static:静态数据成员和普通数据成员区别较大,体现在下面几点:(1)普通数据成员属于类的一个具体的对象,只有对象被创建了,普通数据成员才会被分配内存。而静态数据成员属于整个类,即使没有任何对象创建,类的静态数据成员变量也存在,所有对象只有一份。(2)因为类的静态数据成员的存在不依赖与于任何类对象的存在(没有this指针),类的静态数据成员应该在代码中被显示的初始化,一定要在类外进行,例如上例。(3)外部访问类的静态成员只能通过类名来访问,例如:test::getCount()。(4)类的静态成员函数无法直接访问普通数据成员(可以通过类的指针等作为参数间接访问),而类的任何成员函数都可以访问类的静态数据成员。(5)静态成员和类的普通成员一样,也具有public、protected、private3种访问级别,也可以具有返回值、const修饰符等参数。虚函数、构造函数、析构函数不能为static(显然没有具体对象何来构造析构,静态何以动态重载)。应避免构造函数和析构函数调用虚函数(可能发生未定义行为)。

          值语义(value sematics)指的是对象的拷贝与原对象无关.与之相对的是引用语义。shared_ptr就是值语义,bind和容器都可能拷贝shared_ptr从而意外延长对象的生命周期。

          智能指针作为对象x的数据成员而它的模板参数T是个不完全类型,那么x的析构函数不能是默认或内联的,必须在文件中显式定义。//验证没有报错啊?

同步与异步:同步是直到对方应答,异步是不需要对方应答。打电话是同步,发短信是异步。

          shared_ptr本身并非100%线程安全的,shared_ptr对象实体可能被多个线程同时读取写入,使用mutex保护访问同一个shared_ptr对象global。为了减小临界区的大小,在读取shared_ptr对象时:先创建一个局部shared_ptr对象local然后进入临界区local=global退出临界区然后对local执行读取操作;写入shared_ptr对象时:先创建一个local,进入临界区global=local退出临界区操作local。若要销毁global:则在临界区外定义local,在临界区内执行swap()再在临界区之外销毁local以压缩临界区长度。

          所谓“二进制兼容性”指的就是在升级(也可能是 bug fix)库文件的时候,不必重新编译使用这个库的可执行文件或使用这个库的其他库文件,程序的功能不被破坏。

          当最后一个shared_ptr(指向对象想x)离开其作用域时,x会在这个线程析构(并不一定是x的诞生进程),这个特性可能会拖慢这个关键线程的速度,可以用一个单独的线程专门来做析构。

          RAII:获取资源即初始化==初始化即获取资源,是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。RAII 的一般做法是这样的:在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:1,我们不需要显式地释放资源。2,采用这种方式,对象所需的资源在其生命期内始终保持有效 —— 我们可以说,此时这个类维护了一个 invariant。这样,通过该类对象使用资源时,就不必检查资源有效性的问题,可以简化逻辑、提高效率。最简单的RAII形式是创建这样一个对象:构造函数中获取一份资源,析构函数中则释放资源。例如:class test{};template<typename T>class resourceHandle{resourceHandle(T* t):x(t);~resourceHandle(){delete x;}}在使用test前采用resourceHandle封装它则会不会出现内存泄露。

          shared_ptr使用时避免循环使用,通常owner持有指向child的shared_ptr,child持有指向owner的weak_ptr

          enable_shared_ptr_from_this:是一个以其派生类为模板类型实参的基类模板。继承它this指针就能变身shared_ptr。为了使用share_from_this()派生类不能是栈对象,必须是堆对象且有shared_ptr管理其生命周期。share_from_this()不能在构造函数里调用,因为在构造对象的时候对象还没有交个shared_ptr接管。

你可能感兴趣的:(Linux多线程服务端编程)