1、中断
中断指CPU收到来自硬件或软件的信号,提示发生了某个事件要处理。
中断是计算机中的一个十分重要的概念,在现代计算机中毫无例外地都要采用中断技术。可以举一个日常生活中的例子来说明,假如你正在给朋友写信,电话铃响了。这时,你放下手中的笔,去接电话,通话完毕,再继续写信。在这个例子中,电话铃声称为“中断请求”,你暂停写信去接电话叫作“中断响应”,接电话的过程就是“中断处理”。
计算机为什么要采用中断?为了说明这个问题,再举一例子。假设你有一个朋友来拜访你,但是由于不知道何时到达,你只能在大门等待,于是什么事情也干不了。如果在门口装一个门铃,你就不必在门口等待而去干其它的工作,朋友来了按门铃通知你,你这时才中断你的工作去开门,这样就避免等待和浪费时间。计算机也是一样,例如打印输出,CPU传送数据的速度高,而打印机打印的速度低,如果不采用中断技术,CPU将经常处于等待状态,效率极低。而采用了中断方式,CPU可以进行其它的工作,只在打印机缓冲区中的当前内容打印完毕发出中断请求之后,才予以响应,暂时中断当前工作转去执行向缓冲区传送数据,传送完成后又返回执行原来的程序。这样就大大地提高了计算机系统的效率。
中断可以有优先级,决定在同时发生几个中断的时候,先执行哪个中断响应。如果在响应一个中断,执行中断处理的过程中,又有新的中断事件发生而发出了中断请求,应该如何处理也取决于中断事件的优先级。当新发生的中断事件的优先级高于正在处理的中断事件时,又将中止当前的中断处理程序,转去处理新发生的中断事件,处理完毕才返回原来的中断处理。
2、可重入
可重入的概念是在单线程操作系统的时代提出来的,单线程操作系统下,一个函数的重入可能是由于UNIX系统的Single处理(中断)或者函数的递归调用。 可重入函数简单来说就是可以被中断的函数,也就是说可以在这个函数执行的任何时刻中断它,转入OS调度去执行另外一段代码,且返回函数时执行结果符合设计时的预期。
若一个函数是可重入的,那么该函数:
- ①不能含有静态或全局变量数据,即它除了使用自己栈上的变量以外不依赖于任何环境。
- ②不能依赖于单实例模式资源的锁。
- ③不能调用不可重入的函数。
int sum(int count)
{
static int sum = 0;
for (int i = 1; i <= count; i++)
sum += i;
return sum;
}
由于一些函数是不可重入的,所以当这些函数被中断的时候,比如在其signal信号处理函数中应该尽量简单的去处理,避免在signal处理函数中又调用某些函数,产生可重入问题。
后来又出现了多线程操作系统,所以一个函数的可重入也有可能是当前线程正在调用该方法,而另一个线程同时也可以调用该方法。
int increment_counter ()
{
static int counter = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
++counter;
int result = counter;
pthread_mutex_unlock(&mutex);
return result;
}
竞争:多个线程对于共享数据进行访问,最少有一个线程是进行的写入,并且没有使用同步访问手段,这种情况就会产生数据竞争。避免数据竞争的方法是使用同步锁,在Java中还可以将变量声明为volatile类型来解决竞争问题,因为volatile会保证变量的写操作完成后后面的读操作再执行。如下代码所示,如果去掉锁的话,两个线程执行该方法的话就会发生数据竞争:
void setValue()
{
lock.lock();
g_value++;
lock.unlock();
}
竞态条件:计算的正确性取决于多个线程的交替执行的时序,最常见的竞态条件就是先检测后执行,比如下面的代码,在A线程取得状态为ture后可能另一个线程立即修改了状态为false,而A线程继续以状态为true的前提来做一些事情,这就可能导致出现问题。如果将下面代码中同步锁去掉的话就会既有数据竞争又存在竞态条件:
void func()
{
lock.lock();
bool bFlag = getState();
lock.unlock();
if (bFlag)
{
//ToDo
}
}
解决竞态条件的方法也是使用同步手段:
void func()
{
lock.lock();
bool bFlag = getState();
if (bFlag)
{
//ToDo
}
lock.unlock();
}
5、volatile