搜狗面试题
2015-09-27 15:56
465 查看
c++多态 覆盖与重写
如何实现两个线程互斥,什么时候需要线程互斥
线程死锁,如何线程避免死锁
打破“请求保持”,如果互斥锁怎么实现(lock, unlock, trylock),如何是否已保持资源
参考 《linux c编程一站式学习》
Mutex的两个基本操作lock和unlock是如何实现的呢?假设Mutex变量的值为1表示互斥锁空闲,这时某个进程调用lock可以获得锁,而Mutex的值为0表示互斥锁已经被某个线程获得,其它线程再调用lock只能挂起等待。那么lock和unlock的伪代码如下:
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
lock:
if(mutex > 0){
mutex = 0;
return 0;
} else
挂起等待;
goto lock;
unlock:
mutex = 1;
唤醒等待Mutex的线程;
return 0;
unlock操作中唤醒等待线程的步骤可以有不同的实现,可以只唤醒一个等待线程,也可以唤醒所有等待该Mutex的线程,然后让被唤醒的这些线程去竞争获得这个Mutex,竞争失败的线程继续挂起等待。
细心的读者应该已经看出问题了:对Mutex变量的读取、判断和修改不是原子操作。如果两个线程同时调用lock,这时Mutex是1,两个线程都判断mutex>0成立,然后其中一个线程置mutex=0,而另一个线程并不知道这一情况,也置mutex=0,于是两个线程都以为自己获得了锁。
为了实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的总线周期也有先后,一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。现在我们把lock和unlock的伪代码改一下(以x86的xchg指令为例):
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
lock:
movb $0, %al
xchgb %al, mutex
if(al寄存器的内容 > 0){
return 0;
} else
挂起等待;
goto lock;
unlock:
movb $1, mutex
唤醒等待Mutex的线程;
return 0;
unlock中的释放锁操作同样只用一条指令实现,以保证它的原子性。
也许还有读者好奇,“挂起等待”和“唤醒等待线程”的操作如何实现?每个Mutex有一个等待队列,一个线程要在Mutex上挂起等待,首先把自己加入等待队列中,然后置线程状态为睡眠,接着调用调度器函数切换到别的线程。一个线程要唤醒等待队列中的其它线程,只需从等待队列中取出一项,把它的状态从睡眠改为就绪,加入就绪队列,那么下次调度器函数执行时就有可能切换到被唤醒的线程。
一般情况下,如果同一个线程先后两次调用lock,在第二次调用时,由于锁已经被占用,该线程会挂起等待别的线程释放锁,然而锁正是被自己占用着的,该线程又被挂起而没有机会释放锁,因此就永远处于挂起等待状态了,这叫做死锁(Deadlock)。另一种典型的死锁情形是这样:线程A获得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起自己等待线程B释放锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起自己等待线程A释放锁1,于是线程A和B都永远处于挂起状态了。不难想象,如果涉及到更多的线程和更多的锁,有没有可能死锁的问题将会变得复杂和难以判断。
写程序时应该尽量避免同时获得多个锁,如果一定有必要这么做,则有一个原则:如果所有线程在需要多个锁时都按相同的先后顺序(常见的是按Mutex变量的地址顺序)获得锁,则不会出现死锁,因为至少还有锁是空闲的,某个线程在获得了这个锁,执行完了之后就会释放自己占有的其他资源(包括锁),于是其他线程就可以顺利推进了。比如一个程序中用到锁1、锁2、锁3,它们所对应的Mutex变量的地址是锁1<锁2<锁3,那么所有线程在需要同时获得2个或3个锁时都应该按锁1、锁2、锁3的顺序获得。如果要为所有的锁确定一个先后顺序比较困难,则应该尽量使用pthread_mutex_trylock调用代替pthread_mutex_lock调用,以免死锁。
编程题
实现memcpy函数(注意地址覆盖);
交换树的左右节点(递归+非递归);
linux命令:编写一个shell脚本,实现监控"pro"进程是否在运行,若未运行则重启该进程
设计题:
一个文本中存储单词(每个单词一行),判断某个字符串是否在该文本中;
要求:画出结构图;时间复杂度与空间复杂度;
如何实现两个线程互斥,什么时候需要线程互斥
线程死锁,如何线程避免死锁
打破“请求保持”,如果互斥锁怎么实现(lock, unlock, trylock),如何是否已保持资源
参考 《linux c编程一站式学习》
Mutex的两个基本操作lock和unlock是如何实现的呢?假设Mutex变量的值为1表示互斥锁空闲,这时某个进程调用lock可以获得锁,而Mutex的值为0表示互斥锁已经被某个线程获得,其它线程再调用lock只能挂起等待。那么lock和unlock的伪代码如下:
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
lock:
if(mutex > 0){
mutex = 0;
return 0;
} else
挂起等待;
goto lock;
unlock:
mutex = 1;
唤醒等待Mutex的线程;
return 0;
unlock操作中唤醒等待线程的步骤可以有不同的实现,可以只唤醒一个等待线程,也可以唤醒所有等待该Mutex的线程,然后让被唤醒的这些线程去竞争获得这个Mutex,竞争失败的线程继续挂起等待。
细心的读者应该已经看出问题了:对Mutex变量的读取、判断和修改不是原子操作。如果两个线程同时调用lock,这时Mutex是1,两个线程都判断mutex>0成立,然后其中一个线程置mutex=0,而另一个线程并不知道这一情况,也置mutex=0,于是两个线程都以为自己获得了锁。
为了实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的总线周期也有先后,一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。现在我们把lock和unlock的伪代码改一下(以x86的xchg指令为例):
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
lock:
movb $0, %al
xchgb %al, mutex
if(al寄存器的内容 > 0){
return 0;
} else
挂起等待;
goto lock;
unlock:
movb $1, mutex
唤醒等待Mutex的线程;
return 0;
unlock中的释放锁操作同样只用一条指令实现,以保证它的原子性。
也许还有读者好奇,“挂起等待”和“唤醒等待线程”的操作如何实现?每个Mutex有一个等待队列,一个线程要在Mutex上挂起等待,首先把自己加入等待队列中,然后置线程状态为睡眠,接着调用调度器函数切换到别的线程。一个线程要唤醒等待队列中的其它线程,只需从等待队列中取出一项,把它的状态从睡眠改为就绪,加入就绪队列,那么下次调度器函数执行时就有可能切换到被唤醒的线程。
一般情况下,如果同一个线程先后两次调用lock,在第二次调用时,由于锁已经被占用,该线程会挂起等待别的线程释放锁,然而锁正是被自己占用着的,该线程又被挂起而没有机会释放锁,因此就永远处于挂起等待状态了,这叫做死锁(Deadlock)。另一种典型的死锁情形是这样:线程A获得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起自己等待线程B释放锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起自己等待线程A释放锁1,于是线程A和B都永远处于挂起状态了。不难想象,如果涉及到更多的线程和更多的锁,有没有可能死锁的问题将会变得复杂和难以判断。
写程序时应该尽量避免同时获得多个锁,如果一定有必要这么做,则有一个原则:如果所有线程在需要多个锁时都按相同的先后顺序(常见的是按Mutex变量的地址顺序)获得锁,则不会出现死锁,因为至少还有锁是空闲的,某个线程在获得了这个锁,执行完了之后就会释放自己占有的其他资源(包括锁),于是其他线程就可以顺利推进了。比如一个程序中用到锁1、锁2、锁3,它们所对应的Mutex变量的地址是锁1<锁2<锁3,那么所有线程在需要同时获得2个或3个锁时都应该按锁1、锁2、锁3的顺序获得。如果要为所有的锁确定一个先后顺序比较困难,则应该尽量使用pthread_mutex_trylock调用代替pthread_mutex_lock调用,以免死锁。
编程题
实现memcpy函数(注意地址覆盖);
交换树的左右节点(递归+非递归);
linux命令:编写一个shell脚本,实现监控"pro"进程是否在运行,若未运行则重启该进程
设计题:
一个文本中存储单词(每个单词一行),判断某个字符串是否在该文本中;
要求:画出结构图;时间复杂度与空间复杂度;
相关文章推荐
- java面试题二十七 多线程考题2
- 面试---指针与地址
- 黑马程序员---java基础---多态,抽象类,接口
- 黑马程序员---java基础---代码块及继承
- 美团2016面试总结
- java面试题二十六 多线程考题
- 一个程序员的时间管理“辛”路历程
- 黑马程序员---java基础---面向对象中的构造方法及static关键字,变量的问题
- 赴京面试中的成长
- 黑马程序员———java基础知识———面向对象
- 2016年一些面试题的整理和心情--1异或到100
- 你也一样重要的非技术贴,告诉你的求职信的写作9温馨提示
- java面试题二十五 构造函数
- java面试题二十四 继承题2
- java面试题二十三 接口
- [转载]Java程序员掌握的10大项知识体系--精通太难说出口
- 高效程序员的 7 个共同特征
- 黑马程序员——反射的理解与应用
- Hibernate面试问题集锦: 概述
- 从交换两个变量的值来看程序员的“奇技淫巧”