From c5a105fe3a41da83767a815f867cdfbd7da2053e Mon Sep 17 00:00:00 2001 From: Estom Date: Wed, 21 Apr 2021 15:52:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9C=80=E5=90=8E=E7=9A=84=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- C++/标准库/12 动态内存.md | 63 +++-- C++/标准库/12SmartPtr.cpp | 117 ++++++++ C++/标准库/12allocate.cpp | 105 +++++++ C++/面试/19 内存对齐.md | 33 ++- C++/面试/19.cpp | 23 ++ C++/面试/20 字符串类实现.md | 143 +++++++++- ...组和指针的区别.md => 21.字符数组和字符串.md} | 16 +- C++/面试/22 new&delete.md | 265 ++++++++++++++++++ C++/面试/23 数组参数.md | 42 +++ C++/面试/23.cpp | 28 ++ 工作日志/2021年3月19日-四月份计划.md | 2 +- 工作日志/2021年4月17日-五月份计划.md | 2 +- 工作日志/2021年4月20日-今日计划.md | 13 + 工作日志/2021年4月8日-简历投递记录.md | 11 +- 操作系统/2.1 进程的基本概念.md | 4 +- 操作系统/2.5 进程与线程.md | 2 + 操作系统/附录3 互斥量、信号量、条件变量.md | 9 +- 操作系统/附录5 协程.md | 30 ++ 18 files changed, 872 insertions(+), 36 deletions(-) create mode 100644 C++/标准库/12SmartPtr.cpp create mode 100644 C++/标准库/12allocate.cpp create mode 100644 C++/面试/19.cpp rename C++/面试/{21.数组和指针的区别.md => 21.字符数组和字符串.md} (66%) create mode 100644 C++/面试/22 new&delete.md create mode 100644 C++/面试/23 数组参数.md create mode 100644 C++/面试/23.cpp create mode 100644 工作日志/2021年4月20日-今日计划.md create mode 100644 操作系统/附录5 协程.md diff --git a/C++/标准库/12 动态内存.md b/C++/标准库/12 动态内存.md index 0d39ad7c..f5b3b5d7 100644 --- a/C++/标准库/12 动态内存.md +++ b/C++/标准库/12 动态内存.md @@ -260,12 +260,15 @@ alloc.deallocate(p,n) ```C++ #ifndef __JJALLOC__ #define __JJALLOC__ +#endif #include // for placement new #include //for cerr #include //for ptrdiff_t #include // for exit() #include // for UINT_MAX -namespace JJ{ +namespace my{ + // 申请内存空间。调用operator new 。 + // T*参数是为了注册模板类型T template inline T* _allocate(ptrdiff_t size, T*){ //set_new_handler(0); @@ -275,21 +278,28 @@ namespace JJ{ } return tmp; } - template - inline void _destory(T* ptr){ - ptr->~T(); - } - + // 释放内存空间。调用operator delete template inline void _deallocate(T* buffer){ ::operator delete(buffer); } + // 创建内存对象。调用placement new template inline void _construct(T1 *p, const T2 &value){ - new(p)T1(value); + new (p)T1(value); } + // 通过查询了解到这个操作叫做placement new,就是在指针p所指向的内存空间创建一个T1类型的对象,但是对象的内容是从T2类型的对象转换过来的(调用了T1的构造函数,T1::T1(value))。 + // 就是在已有空间的基础上重新调整分配的空间,类似于realloc函数。这个操作就是把已有的空间当成一个缓冲区来使用,这样子就减少了分配空间所耗费的时间,因为直接用new操作符分配内存的话,在堆中查找足够大的剩余空间速度是比较慢的。 + + // 释放内存对象。调用析构函数。 + template + inline void _destroy(T* ptr){ + ptr->~T(); + } + + template class allocate{ public: @@ -299,14 +309,14 @@ namespace JJ{ typedef T& reference; typedef const T& const_reference; typedef size_t size_type; - typedef ptrdiff_t diference_type; + typedef ptrdiff_t difference_type; - template - struct rebind{ - typedef allocator other; - }; + // template + // struct rebind{ + // typedef allocator other; + // }; - pointer allocate(size_type n, const void * hint = 0){ + pointer alloc(size_type n, const void * hint = 0){ return _allocate((difference_type)n, (pointer)0); } void deallocate(pointer p, size_type n){ @@ -332,6 +342,27 @@ namespace JJ{ } }; } +#include +using namespace std; +int main(){ + my::allocate al; + int * ptr = al.alloc(10); + cout<<"alloc:"<<*ptr<<"\t"<<*(ptr+1)<::operator-(SmartPtr &t1, SmartPtr &t2) { -// return t1.ptr - t2.ptr; -// } +int SmartPtr::operator-(SmartPtr &t1, SmartPtr &t2) { + return t1.ptr - t2.ptr; + } template SmartPtr::SmartPtr(T *p) diff --git a/C++/标准库/12SmartPtr.cpp b/C++/标准库/12SmartPtr.cpp new file mode 100644 index 00000000..5a01ca94 --- /dev/null +++ b/C++/标准库/12SmartPtr.cpp @@ -0,0 +1,117 @@ +template +class SmartPtr +{ +private: + T *ptr; //底层真实的指针 + + int *use_count; //保存当前对象被多少指针引用计数 + +public: + SmartPtr(T *p); //SmartPtrp(new int(2)); + + SmartPtr(const SmartPtr &orig); //SmartPtrq(p); + + SmartPtr &operator=(const SmartPtr &rhs); //q=p + + ~SmartPtr(); + + T operator*(); //为了能把智能指针当成普通指针操作定义解引用操作 + + T *operator->(); //定义取成员操作 + + T *operator+(int i); //定义指针加一个常数 + + int operator-(SmartPtr &t1); //定义两个指针相减。当定义成友元函数的时候,必须有两个参数。当定义成类的成员函数的时候。相当于调用函数,智能有一个参数。 + + int getcount() { return *use_count; }; +}; + + +template +int SmartPtr::operator-(SmartPtr &t) { + return ptr - t.ptr; + } + +template +SmartPtr::SmartPtr(T *p) +{ + ptr = p; + try + { + use_count = new int(1); + } + catch (...) + { + delete ptr; //申请失败释放真实指针和引用计数的内存 + + ptr = nullptr; + delete use_count; + use_count = nullptr; + } +} +template +SmartPtr::SmartPtr(const SmartPtr &orig) //复制构造函数 + +{ + + use_count = orig.use_count; //引用计数保存在一块内存,所有的SmarPtr对象的引用计数都指向这里 + + this->ptr = orig.ptr; + + ++(*use_count); //当前对象的引用计数加1 +} +template +SmartPtr &SmartPtr::operator=(const SmartPtr &rhs) +{ + //重载=运算符,例如SmartPtrp,q; p=q;这个语句中,首先给q指向的对象的引用计数加1,因为p重新指向了q所指的对象,所以p需要先给原来的对象的引用计数减1,如果减一后为0,先释放掉p原来指向的内存,然后讲q指向的对象的引用计数加1后赋值给p + + ++*(rhs.use_count); + if ((--*(use_count)) == 0) + { + delete ptr; + ptr = nullptr; + delete use_count; + use_count = nullptr; + } + ptr = rhs.ptr; + *use_count = *(rhs.use_count); + return *this; +} +template +SmartPtr::~SmartPtr() +{ + getcount(); + if (--(*use_count) == 0) //SmartPtr的对象会在其生命周期结束的时候调用其析构函数,在析构函数中检测当前对象的引用计数是不是只有正在结束生命周期的这个SmartPtr引用,如果是,就释放掉,如果不是,就还有其他的SmartPtr引用当前对象,就等待其他的SmartPtr对象在其生命周期结束的时候调用析构函数释放掉 + + { + getcount(); + delete ptr; + ptr = nullptr; + delete use_count; + use_count = nullptr; + } +} +template +T SmartPtr::operator*() +{ + return *ptr; +} +template +T *SmartPtr::operator->() +{ + return ptr; +} +template +T *SmartPtr::operator+(int i) +{ + T *temp = ptr + i; + return temp; +} + +#include +using namespace std; +int main(){ + SmartPtr ptr(new int(123)); + cout<<*ptr< // for placement new +#include //for cerr +#include //for ptrdiff_t +#include // for exit() +#include // for UINT_MAX +namespace my{ + // 申请内存空间。调用operator new 。 + // T*参数是为了注册模板类型T + template + inline T* _allocate(ptrdiff_t size, T*){ + //set_new_handler(0); + T* tmp = (T*)(::operator new)((size_t)(size * sizeof(T))); + if (tmp == 0){ + std::cerr << "out of memory" << std::endl; + } + return tmp; + } + + // 释放内存空间。调用operator delete + template + inline void _deallocate(T* buffer){ + ::operator delete(buffer); + } + + // 创建内存对象。调用placement new + template + inline void _construct(T1 *p, const T2 &value){ + new (p)T1(value); + } + // 通过查询了解到这个操作叫做placement new,就是在指针p所指向的内存空间创建一个T1类型的对象,但是对象的内容是从T2类型的对象转换过来的(调用了T1的构造函数,T1::T1(value))。 + // 就是在已有空间的基础上重新调整分配的空间,类似于realloc函数。这个操作就是把已有的空间当成一个缓冲区来使用,这样子就减少了分配空间所耗费的时间,因为直接用new操作符分配内存的话,在堆中查找足够大的剩余空间速度是比较慢的。 + + // 释放内存对象。调用析构函数。 + template + inline void _destroy(T* ptr){ + ptr->~T(); + } + + + template + class allocate{ + public: + typedef T value_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + // template + // struct rebind{ + // typedef allocator other; + // }; + + pointer alloc(size_type n, const void * hint = 0){ + return _allocate((difference_type)n, (pointer)0); + } + void deallocate(pointer p, size_type n){ + _deallocate(p); + } + + void construct(pointer p, const_reference value){ + return _construct(p, value); + } + + void destroy(pointer p){ + _destroy(p); + } + pointer address(reference x){ + return (pointer)&x; + } + pointer const_address(const_reference x){ + return (const_pointer)&x; + } + + size_type max_size()const{ + return (size_type)(UINT_MAX / sizeof(T)); + } + }; +} +#include +using namespace std; +int main(){ + my::allocate al; + int * ptr = al.alloc(10); + cout<<"alloc:"<<*ptr<<"\t"<<*(ptr+1)< 2 按2对齐;偏移量为0;存放位置区间[0,3] + +char b; //长度1 < 2 按1对齐;偏移量为4;存放位置区间[4] + +short c; //长度2 = 2 按2对齐;偏移量要提升到2的倍数6;存放位置区间[6,7] + +char d; //长度1 < 2 按1对齐;偏移量为7;存放位置区间[8];共九个字节 + +}; + +#pragma pack() +``` \ No newline at end of file diff --git a/C++/面试/19.cpp b/C++/面试/19.cpp new file mode 100644 index 00000000..b33bd820 --- /dev/null +++ b/C++/面试/19.cpp @@ -0,0 +1,23 @@ +#include + +using namespace std; + +// 测试结构体内存对齐 +struct data +{ + char a='1'; + char b='2'; + char c='3'; + char h='5'; + char e='6'; + int d = 4; + +}; + +int main(){ + struct data d; + cout<> 运算符 +* 6. 重载比较运算符 +* 7. 重载[]下标运算符 +*/ + +#include +#include +using namespace std; + +class MyString +{ +private: + char * str; + int length; +public: + // 长度 + int size ()const { + return length; + }; + char* getstr()const{ + return str; + } + // 默认构造函数 + MyString(); + // 字符串构造函数 + MyString(const char*); + // 复制构造函数 + MyString(const MyString& b); + + // 重载等号运算符 + MyString& operator=(const MyString &b); + // 重载+=运算符 + MyString& operator+=(const MyString &b); + // 重载比较运算符 + bool operator<(const MyString &b); + // 重载下标运算符 + char& operator[](const int &index) const ; + // 重载输入输出操作 + friend ostream& operator<<(ostream& ,const MyString &b); + ~MyString(); +}; + +MyString::MyString() +{ + str = new char[1]; + str[0]='\0'; + length = 0; +} + +MyString::MyString(const char* b){ + if(b){ + length = strlen(b); + str = new char[length+1]; + strcpy(str,b); + } + else{ + MyString(); + } +} +MyString::MyString(const MyString&b){ + length = b.size(); + if(length>0) + str = new char[length+1]; + else + MyString(); +} + +MyString& MyString::operator=(const MyString &b){ + if(&b == this){ + return *this; + } + delete[] str; + length = b.size(); + str = new char[length + 1]; + strcpy(str,b.getstr()); + return *this; +} + +MyString& MyString::operator+=(const MyString&b){ + if(b.size()==0){ + return *this; + } + char* temp = new char[length+b.length+1]; + strcpy(temp,str); + strcat(temp,b.getstr()); + delete[] str; + str = temp; + return *this; +} + +char& MyString::operator[](const int &index)const { + if(index>length)return str[length]; + return str[index]; +} + +bool MyString::operator<(const MyString &b){ + for(int i=0;ib.size())return false; + if(b[i]>str[i])return true; + if(b[i] 参考文献 +> * [https://www.cnblogs.com/luxiaoxun/archive/2012/08/10/2631812.html](https://www.cnblogs.com/luxiaoxun/archive/2012/08/10/2631812.html) +> * [https://blog.csdn.net/linuxheik/article/details/80449059](https://blog.csdn.net/linuxheik/article/details/80449059) + + +> new operator/delete operator就是new和delete操作符。而operator new/operator delete是全局函数。 + + +## 1 C++中的new/delete + +new operator就是new操作符,**不能被重载**,假如A是一个类,那么A * a=new A;实际上执行如下3个过程。 +1. 调用operator new分配内存,operator new (sizeof(A)) +2. 调用构造函数生成类对象,A::A() +3. 返回相应指针 + + +## 2 operator new/operator delete +### 三种形式 +operator new是函数,分为三种形式(前2种不调用构造函数,这点区别于new operator): +```C++ +void* operator new (std::size_t size) throw (std::bad_alloc); +void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) throw(); +void* operator new (std::size_t size, void* ptr) throw(); +``` +1. 第一种分配size个字节的存储空间,并将对象类型进行内存对齐。如果成功,返回一个非空的指针指向首地址。失败抛出bad_alloc异常。 +2. 第二种在分配失败时不抛出异常,它返回一个NULL指针。 +3. 第三种是placement new版本,它本质上是对operator new的重载,定义于#include 中。它不分配内存,调用合适的构造函数在ptr所指的地方构造一个对象,之后返回实参指针ptr。 + +### 重载operator new +第一、第二个版本可以被用户重载,定义自己的版本,第三种placement new不可重载。 + +1. 重载时,返回类型必须声明为void* +2. 重载时,第一个参数类型必须为表达要求分配空间的大小(字节),类型为size_t +3. 重载时,可以带其它参数 + + +### 实例 +```C++ +#include +#include +using namespace std; + +class X +{ +public: + X() { cout<<"constructor of X"< +using namespace std; + +class A +{ +public: + A() + { + cout << "A's constructor" << endl; + } + + + ~A() + { + cout << "A's destructor" << endl; + } + + void show() + { + cout << "num:" << num << endl; + } + +private: + int num; +}; + +int main() +{ + char mem[100]; + mem[0] = 'A'; + mem[1] = '\0'; + mem[2] = '\0'; + mem[3] = '\0'; + cout << (void*)mem << endl; + A* p = new (mem)A; + cout << p << endl; + p->show(); + p->~A(); + getchar(); +} +``` + +1. 用定位放置new操作,既可以在栈(stack)上生成对象,也可以在堆(heap)上生成对象。如本例就是在栈上生成一个对象。 +2. 使用语句A* p=new (mem) A;定位生成对象时,指针p和数组名mem指向同一片存储区。所以,与其说定位放置new操作是申请空间,还不如说是利用已经请好的空间,真正的申请空间的工作是在此之前完成的。 +3. 使用语句A *p=new (mem) A;定位生成对象时,会自动调用类A的构造函数,但是由于对象的空间不会自动释放(对象实际上是借用别人的空间),所以必须显示的调用类的析构函数,如本例中的p->~A()。 + +## 4 new 、operator new 和 placement new 区别 + +### 1 new :不能被重载,其行为总是一致的。它先调用operator new分配内存,然后调用构造函数初始化那段内存。 + +new 操作符的执行过程: +1. 调用operator new分配内存 ; +2. 调用构造函数生成类对象; +3. 返回相应指针。 + +### 4 operator new:要实现不同的内存分配行为,应该重载operator new,而不是new。 + +* operator new就像operator + 一样,是可以重载的。如果类中没有重载operator new,那么调用的就是全局的::operator new来完成堆的分配。同理,operator new[]、operator delete、operator delete[]也是可以重载的。 + +* placement new:只是operator new重载的一个版本。它并不分配内存,只是返回指向已经分配好的某段内存的一个指针。因此不能删除它,但需要调用对象的析构函数。 + +* 如果你想在已经分配的内存中创建一个对象,使用new时行不通的。也就是说placement new允许你在一个已经分配好的内存中(栈或者堆中)构造一个新的对象。原型中void* p实际上就是指向一个已经分配好的内存缓冲区的的首地址。 + + +## 5 实例——Placement new使用步骤 + +在很多情况下,placement new的使用方法和其他普通的new有所不同。这里提供了它的使用步骤。 + +### 第一步 缓存提前分配 + +有三种方式: + +1. 为了保证通过placement new使用的缓存区的memory alignment(内存队列)正确准备,使用普通的new来分配它:在堆上进行分配 + +``` +class Task ; +char * buff = new [sizeof(Task)]; //分配内存 +(请注意auto或者static内存并非都正确地为每一个对象类型排列,所以,你将不能以placement new使用它们。) +``` + +1. 在栈上进行分配 +``` +class Task ; +char buf[N*sizeof(Task)]; //分配内存 +``` +3. 还有一种方式,就是直接通过地址来使用。(必须是有意义的地址) +``` +void* buf = reinterpret_cast (0xF00F); +``` +### 第二步:对象的分配 + +在刚才已分配的缓存区调用placement new来构造一个对象。 +``` +Task *ptask = new (buf) Task +``` +### 第三步:使用 + +按照普通方式使用分配的对象: +``` +ptask->memberfunction(); + +ptask-> member; + +//... +``` +### 第四步:对象的析构 + +一旦你使用完这个对象,你必须调用它的析构函数来毁灭它。按照下面的方式调用析构函数: +``` +ptask->~Task(); //调用外在的析构函数 +``` +### 第五步:释放 + +你可以反复利用缓存并给它分配一个新的对象(重复步骤2,3,4)如果你不打算再次使用这个缓存,你可以象这样释放它:delete [] buf; + +跳过任何步骤就可能导致运行时间的崩溃,内存泄露,以及其它的意想不到的情况。如果你确实需要使用placement new,请认真遵循以上的步骤。 + +### 代码实现 +```C++ +#include +using namespace std; + +class X +{ +public: + X() { cout<<"constructor of X"<SetNum(10); + cout<GetNum()<~X(); + delete []buf; + + return 0; +} +``` \ No newline at end of file diff --git a/C++/面试/23 数组参数.md b/C++/面试/23 数组参数.md new file mode 100644 index 00000000..c667de86 --- /dev/null +++ b/C++/面试/23 数组参数.md @@ -0,0 +1,42 @@ + +## 数组参数 + +### 三种形式 + +void test1(int *s) +void test2(int s[]) +void test3(int s[5]) + + +### 实例 + +``` +#include +#include +using namespace std; + +// 测试字符串和字符数组的参数传递 +void test1(int *s){ + cout<<*(s)< +#include +using namespace std; + +// 测试字符串和字符数组的参数传递 +void test1(int *s){ + cout<<*(s)< 第八周任务:接受学弟的论文工作内容。完成基础三篇论文的复现工作。 + +- [ ] 打理好生活(洗衣服、洗澡、准备开始跑步)。吃饭时间会宿舍洗衣服洗澡。 +- [ ] 面试中相关问题的处理。协程?数据库性能优化?C++性能优化?服务器性能优化。 +- [ ] 晚上回宿舍洗衣服。 +- [ ] 完成之前所有的任务。跑通TensorFlow。 +- [ ] 完成pysyft框架学习 +- [ ] 完成TensorFlow federated框架的学习。 + + +## 收获 \ No newline at end of file diff --git a/工作日志/2021年4月8日-简历投递记录.md b/工作日志/2021年4月8日-简历投递记录.md index e980022e..76bf35ec 100644 --- a/工作日志/2021年4月8日-简历投递记录.md +++ b/工作日志/2021年4月8日-简历投递记录.md @@ -17,7 +17,7 @@ - [x] 2021年4月8日16:00 1面。准备以上内容。 - [x] 2021年4月9日10:30 1面。面试改到北京了 - [x] 2021-04-13 10:30 2面。面试,项目与基础知识 - - [ ] 04-18 20:00:00 -- 22:00:00 笔试 + - [x] ~~04-18 20:00:00 -- 22:00:00 笔试~~ > 但是TMD之前的面试进度还在。没办法参加第二次面试了,早知道,直接换个事业群,换一波人说不定还好说话。妈卖批。别是上一个boss ## ~~商汤科技~~ @@ -73,7 +73,7 @@ - [x] 简历投递https://campus.alibaba.com/myJobApply.htm - [x] 素质测评 - [x] 2021年04月09日 19:00 - 2021年04月09日 20:00。完球了没人捞我的专利,待会问问师兄。 - - [ ] 2021年4月19日 16:00 一面。终于还是来了。还是要好好准备的。把所有的问题复习一遍。估计很难进。 + - [x] 2021年4月19日 16:00 一面。终于还是来了。还是要好好准备的。把所有的问题复习一遍。估计很难进。 > 二期简历 @@ -89,7 +89,7 @@ 4. 对数据结构、算法有一定了解; 5. 优选条件:熟悉TCP/IP协议及互联网常见应用和协议的原理;有IT应用软件、互联网软件、IOS/安卓等相关产品开发经验,不满足于课堂所学,在校期间积极参加校内外软件编程大赛或积极参于编程开源社区组织;熟悉JS/AS/AJAX/HTML5/CSS等前端开发技术。 * 流程 - - [ ] 简历投递https://career.huawei.com/reccampportal/portal5/user-index.html + - [x] 简历投递https://career.huawei.com/reccampportal/portal5/user-index.html ## 美团 @@ -101,7 +101,7 @@ 4. 优秀的逻辑思维能力,特别是流程梳理能力和建模能力,善于从复杂系统表象中分析问题。具有较强的解决问题能力,对解决复杂问题充满激情; 5. 善于交流,有良好的团队合作精神和协调沟通能力,有一定推动能力。 * 流程 - - [ ] 简历投递https://campus.meituan.com/apply-record + - [x] 简历投递https://campus.meituan.com/apply-record ## 快手 @@ -115,6 +115,7 @@ 6. 具有良好的沟通能力和团队合作精神、优秀的分析问题和解决问题的能力。 * 流程 - [x] 简历投递https://zhaopin.kuaishou.cn/recruit/e/#/official/my-apply/ + - [x] 2021-04-19 18:00:00 1面 ## 网易 * 岗位:C++开发实习生 * 岗位要求 @@ -125,5 +126,5 @@ 5. 十八般武艺样样精通,掌握多线程并发编程技术,掌握各种数据结构和算法; 6. 熟悉windows/linux编程环境,如果有MySQL开发经验那就再 好 不 过 啦!!! * 流程 - - [ ] 简历投递https://campus.163.com/app/personal/apply + - [x] 简历投递https://campus.163.com/app/personal/apply diff --git a/操作系统/2.1 进程的基本概念.md b/操作系统/2.1 进程的基本概念.md index 4b4bc00a..7c24144f 100644 --- a/操作系统/2.1 进程的基本概念.md +++ b/操作系统/2.1 进程的基本概念.md @@ -133,4 +133,6 @@ ![](image/2021-03-30-12-57-26.png) 2. 索引方式系统根据所有进程的状态建立几张索引表。例如,就绪索引表、阻塞索引表等。 -![](image/2021-03-30-12-58-03.png) \ No newline at end of file +![](image/2021-03-30-12-58-03.png) + + diff --git a/操作系统/2.5 进程与线程.md b/操作系统/2.5 进程与线程.md index 09608612..b637e59f 100644 --- a/操作系统/2.5 进程与线程.md +++ b/操作系统/2.5 进程与线程.md @@ -61,3 +61,5 @@ + + diff --git a/操作系统/附录3 互斥量、信号量、条件变量.md b/操作系统/附录3 互斥量、信号量、条件变量.md index e1a5ad69..5fdcb64c 100644 --- a/操作系统/附录3 互斥量、信号量、条件变量.md +++ b/操作系统/附录3 互斥量、信号量、条件变量.md @@ -1,4 +1,4 @@ -# 互斥朗、信号量、条件变量.md +# 互斥朗、信号量、条件变量 > 参考文献 > * [http://blog.chinaunix.net/uid-20205875-id-4865684.html](http://blog.chinaunix.net/uid-20205875-id-4865684.html) @@ -121,7 +121,7 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex); * 读写锁可以由三种状态:读模式下加锁状态、写模式下加锁状态、不加锁状态。一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。 * 在读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞。当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是如果线程希望以写模式对此锁进行加锁,它必须阻塞直到所有的线程释放读锁。虽然读写锁的实现各不相同,但当读写锁处于读模式锁住状态时,如果有另外的线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁请求。这样可以避免读模式锁长期占用,而等待的写模式锁请求一直得不到满足。 * 读写锁非常适合于对数据结构读的次数远大于写的情况。当读写锁在写模式下时,它所保护的数据结构就可以被安全地修改,因为当前只有一个线程可以在写模式下拥有这个锁。当读写锁在读状态下时,只要线程获取了读模式下的读写锁,该锁所保护的数据结构可以被多个获得读模式锁的线程读取。 -* 读写锁也叫做共享-独占锁,当读写锁以读模式锁住时,它是以共享模式锁住的;当他以写模式锁住时,它是以独占模式锁住的。 +* **读写锁也叫做共享-独占锁**,当读写锁以读模式锁住时,它是以**共享模式锁住**的;当他以写模式锁住时,它是以**独占模式锁住**的。 ### 实现——读写锁 > 初始化和销毁 @@ -228,8 +228,3 @@ int pthread_cond_broadcast(pthread_cond_t *cond); * 这两个函数用于通知线程条件已经满足. 调用这两个函数, 也称向线程或条件发送信号. 必须注意, 一定要在改变条件状态以后再给线程发送信号. -## 5 管程 - - -## 6 协程 - diff --git a/操作系统/附录5 协程.md b/操作系统/附录5 协程.md new file mode 100644 index 00000000..4206d149 --- /dev/null +++ b/操作系统/附录5 协程.md @@ -0,0 +1,30 @@ +# 协程 + +## 1 概念 + +协程,又称微线程,纤程,英文名Coroutine。协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。 + +例如: +``` +def A() : +print '1' +print '2' +print '3' +def B() : +print 'x' +print 'y' +print 'z' +``` +由协程运行结果可能是12x3yz。在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A。但协程的特点在于是一个线程执行。 + +## 2 区别 + +1. 那和多线程比,协程最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。 + +2. 第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。 + +## 3 其他 + +在协程上利用多核CPU呢——多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。 + +Python对协程的支持还非常有限,用在generator中的yield可以一定程度上实现协程。虽然支持不完全,但已经可以发挥相当大的威力了。 \ No newline at end of file