Tipsaa

什么是惊群?

epoll的惊群?

什么是虚假唤醒?

lock_guard和unique_lock两种实现RAII的锁std::unique_lock, std::lock, std::scoped_lock

scoped_lock,shared_lock?

  // 锁定两个互斥而不死锁
    std::lock(from.m, to.m);
    // 保证二个已锁定互斥在作用域结尾解锁
    std::lock_guard<std::mutex> lock1(from.m, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(to.m, std::adopt_lock);
 
// 等价方法:
//    std::unique_lock<std::mutex> lock1(from.m, std::defer_lock);
//    std::unique_lock<std::mutex> lock2(to.m, std::defer_lock);
//    std::lock(lock1, lock2);

std::lock一次性获取两个互斥锁,lock_guard默认会在构造函数内锁住互斥锁,std::adopt_lock表明此时互斥锁已经锁住,构造函数将不会再获取这个互斥锁。lock_guard保证了即使发生了异常也能正常释放互斥锁。对于std::lock调用,如果一个锁成功获取后,另一个锁获取失败,将抛出一个异常,并且已经获取成功的锁将释放。
 

写一个三个线程循环打印数字,从0到100依次打印,类似abcabcabc这样打印,不过这个是数字依次+1变化?

我当初自己写的一版代码,其实可以不要flg,直接用i%3取模判断

mutex mt;
condition_variable cond;
int i=0;
int n=100;
int flg=1;//a1 b2 c3  标记
void funa()//0 3  6
{
    unique_lock<mutex>lock (mt);
   // unique_lock(mutex)lock(mt,defer_lock);
   // lock.lock();
    while(i<=n)
    {
        this_thread::sleep_for(chrono::milliseconds(400));
        while(flg!=1)
        {
            cond.wait(lock);
        }
        cout<<"funa: "<<i<<endl;
        i++;
        flg=2;
        cond.notify_one();//cond.notify_all();
    }
}
void funb()//1  4  7
{ 
    unique_lock<mutex>lock (mt);
    while(i<=n)
    {
        this_thread::sleep_for(chrono::milliseconds(400));
        while(flg!=2)
        {
            cond.wait(lock);
        }
        cout<<"funb: "<<i<<endl;
        i++;
        flg=3;
        cond.notify_one();
    }
}

void func()//2  5  8
{
      unique_lock<mutex>lock (mt);
    while(i<=n)
    {
        this_thread::sleep_for(chrono::milliseconds(400));
        while(flg!=3)
        {
            cond.wait(lock);
        }
        cout<<"func: "<<i<<endl;
        i++;
        flg=1;
        cond.notify_one();
    }
}
int main()
{
    thread tha(funa);
    thread thb(funb);
    thread thc(func);
    tha.join();
    thb.join();
    thc.join();
}

分析一下为什么 打印出来会发现打印到了102;多出俩101  102?

cond.wait 先解锁,在把他放进cond的等待队列里面,notify如果唤醒,就是把cond等待队列里面的唤醒到互斥锁mtx的等待队列里面,假如说是a打印的最后一个等于n的数字,那么a线程正常结束,释放资源,锁就没了,在等待队列里的bc就会从阻塞的while flg判断里出来往下走,打印101,102;具体谁先走一般谁抢到了谁走,不过一般像这种队列里,谁前谁走,也是因为这样我上面我通知写的是通知一个的notify_one 

改成这样就能完美打印了 

void funa()//0 3  6
{
    unique_lock<mutex>lock (mt);
   // unique_lock(mutex)lock(mt,defer_lock);
   // lock.lock();
    while(i<=n)
    {
        this_thread::sleep_for(chrono::milliseconds(400));
        while(i<=n&&flg!=1)
        {
            cond.wait(lock);
        }
        if(i>n)break;//防止最后的死锁在里面了
        cout<<"funa: "<<i<<endl;
        i++;
        flg=2;
        cond.notify_one();//cond.notify_all();
    }
}
void funb()//1  4  7
{ 
    unique_lock<mutex>lock (mt);
    while(i<=n)
    {
        this_thread::sleep_for(chrono::milliseconds(400));
        while(i<=n&&flg!=2)
        {
            cond.wait(lock);
        }
        if(i>n)break;//防止最后的死锁在里面了
        cout<<"funb: "<<i<<endl;
        i++;
        flg=3;
        cond.notify_one();
    }
}

void func()//2  5  8
{
    unique_lock<mutex>lock (mt);
    while(i<=n)
    {
        this_thread::sleep_for(chrono::milliseconds(400));
        while(i<=n&&flg!=3)
        {
            cond.wait(lock);
        }
         if(i>n)break;//防止最后的死锁在里面了
        cout<<"func: "<<i<<endl;
        i++;
        flg=1;
        cond.notify_one();
    }
}

协程?协程的概念,为什么要用协程,以及协程的使用_someone丶的博客-CSDN博客_为什么要用协程

乐观锁悲观锁?互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景_小林coding的博客-CSDN博客_各种锁应用场景https://blog.csdn.net/qq_34827674/article/details/108608566?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167171530016800192212252%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167171530016800192212252&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-108608566-null-null.142%5Ev68%5Epc_rank_34_queryrelevant25,201%5Ev4%5Eadd_ask,213%5Ev2%5Et3_esquery_v1&utm_term=%E6%82%B2%E8%A7%82%E9%94%81%E4%B9%90%E8%A7%82%E9%94%81%E8%87%AA%E6%97%8B%E9%94%81&spm=1018.2226.3001.4187

悲观锁:顾名思义,就是比较悲观的锁,总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。

乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。

 

无锁栈,无锁队列?

无锁队列的实现 | 酷 壳 - CoolShell

无锁队列概述_jamin_liu_91的博客-CSDN博客_无锁队列

(16条消息) CAS原理及其优缺点、总线锁、缓存锁_初心江湖路的博客-CSDN博客_cas优点

(31条消息) 多生产者多消费者问题的无锁队列实现_总想玩世不恭的博客-CSDN博客_多生产者多消费者 无锁队列

无锁队列适用的场景? 

数据量大,一秒处理非常多的数据的时候,处理海量数据

无锁队列的根本是cas

无锁队列要解决的问题:

  1. cache失效
  2. 竞争导致线程的切换
  3. 内存的频繁申请和释放

无锁队列原理及实现(一)_小鲤鱼与绿的博客-CSDN博客_无锁队列实现原理

linux内核kfifo无锁队列讲解

银行告诉你什么是无锁队列 - 知乎 (zhihu.com)

linux查看进程  

linux查看cpu占用

linux查看内存占用

c++ class和struct

c++/c struct

内存对齐

c++哪些函数不能被声明为虚函数?

智能指针有没有内存泄露的情况?  有,循环引用就会造成智能指针有没有内存泄露的情况?_

问到tcp粘包问题的时候?

应该这么回答:去站在协议本身和程序员的立场上去谈这个问题。tcp协议本身是没问题的,他是按照流式去传输的,而因为我们写代码的时候数据是按照包的形式发送的,并且每次发送的数据包的大小也可能不一样,所以出现粘包问题是因为我们程序员自己的需求导致的,而不是 tcp协议本身的问题

解决方案:

1.使用标准的应用层协议(比如:http、https)来封装要传输的不定长的数据包

2.在每条数据的尾部添加特殊字符,如果遇到特殊字符,代表当条数据接收完毕了

有缺陷:效率低,需要一个字节一个字节接收,接收一个字节判断一次,判断是不是那个特殊字符串

3.在发送数据块之前,在数据块最前边添加一个固定大小的数据头,这时候数据由两部分组成:数据头 + 数据块

数据头:存储当前数据包的总字节数,接收端先接收数据头,然后在根据数据头接收对应大小的字节

数据块:当前数据包的内容

聊聊网络io

  1. 阻塞io (NIO)
  2. 非阻塞io (BIO)
  3. io多路复用 select/poll
  4. io多路复用 epoll
  5. 信号驱动io
  6. 异步io

c10k问题?

1、C10K 问题。单机一万并发量

2、并发10k 和100 的区别关键在于CPU

3、创建的进程线程多了,数据拷贝频繁(缓冲I/O、内核将数据拷贝到用户进程空间、阻塞), 进程/线程上下文切换消耗大,导致系统奔溃。

4、解决C10K问题的关键就是尽可能减少这些CPU等核心计算资源消耗,从而榨干单台服务器的性能,突破C10K问题所描述的瓶颈。

(274条消息) C10k问题简述_爱思考的实践者的博客-CSDN博客_c10k
 

Reactor和Proactor ?

reactor是非阻塞同步的网络模式,非阻塞同步是啥呢,就是内核数据准备好的这一段我们不等待,等他准备好了,通知我们准备ok了,我们用户主动去调用read write这些函数去处理准备好的数据,内核将数据从内核空间拷贝到用户空间的过程都是需要等待的,也就是说这个过程是同步的

proactor是异步的网络模式,异步是啥呢?内核数据准备好」和「数据从内核态拷贝到用户态都是自动的,不需要等待,等着回调函数回调通知我们最终完成的结果就行

非阻塞同步Reactor 模式就是快递员在楼下,给你打电话告诉你快递到你家小区了,你需要自己下楼来拿快递。而在 异步Proactor 模式下,快递员直接将快递送到你家门口,然后通知你。

有关信号量pv操作?

(283条消息) 操作系统——PV操作_ws_Ando的博客-CSDN博客_操作系统pv操作

(101条消息) CPU与Cache、内存以及硬盘之间的数据交换_尚若水的博客-CSDN博客_cache和主存交换数据的过程

内存屏障?

简单说一说空间配置器

一级的话是大于128字节调用malloc申请

二级是小于128字节,调用。底层是一个自由链表和内存池实现的,底层维护了16条链表,0-15编号,每一条链表根据8字节递增,当我们申请内存的时候根据大小去转到对应编号的链表上。看链表上挂的内存空不空,不空的话看内存够不够,够的话分配出去,指针往后移动;如果链表空了,就看内存池空不空,内存池不空,就分配一些个结点大小的内存,把一个给用户去用,剩下的挂在那个链表上方便后面去取;如果内存池不够分配或者空的话,就会使用malloc()从堆上申请内存,一半拿去用,一半就给内存池;如果堆内存不够给内存池分配,那么二级配置器就会搜索链表调到下一个编号的链表上去给用户分配内存;如果有就分配,如果最后还没有找到能给分配的链表的话就调用一级配置器malloc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BearPot

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值