在关键部分可以进行上下文切换吗?

时间:2010-06-23 16:31:11

标签: multithreading

最近我读了一篇有趣的博客,比较互斥和信号量:

http://www.feabhas.com/blog/2009/09/mutex-vs-semaphores-%E2%80%93-part-1-semaphores/

引用它:

如果在该任务处于关键区域时发生上下文切换,而另一个任务也在P(S)上调用,那么将通过处于等待状态来阻止该第二任务(以及任何后续任务)进入关键区域由操作系统。稍后将重新安排第一个任务并调用V(S)以指示它已离开关键区域。现在将允许第二个任务访问关键区域。 “

如果信号量的确如此,那么互斥量也是如此吗?我不认为它是真的,好像一块代码被锁定,它应该是“原子的”,不能被上下文切换或中断。我是对的吗?

4 个答案:

答案 0 :(得分:12)

不,上下文切换几乎可以在任何地方发生。虽然通常最好尽可能短时间地保持锁定,但是你不希望你的整个机器只是因为一个进程有多个线程持有锁而有核心,等待某些事情发生,你会不会?

锁定的目的是防止可能干扰锁中代码的代码被执行 - 它不会在系统的每个进程中阻止所有其他代码被执行。 (毕竟,上下文切换到不同的进程仍然是一个上下文切换。)

答案 1 :(得分:6)

这取决于您正在处理的“关键部分”的版本。例如,在OS / 2(首先包含关键部分的当前Windows的前身)中,进入关键部分阻止了在同一进程中切换到不同的线程。在Windows NT中,他们更改了这一点,因此允许进行线程切换,因此只有在/尝试进入相同的关键部分时才会阻止其他线程。

在这两种情况下,关键部分都是进程的本地部分,因此不同进程中的线程永远不会尝试进入同一个关键部分。

在其他系统上,您必须查看关键部分(假设它有一个部分)是如何指定的,以了解它允许/禁止的内容。没有通用的定义。

答案 2 :(得分:0)

在Linux中始终可以进行上下文切换,不能独占CPU,但是获取锁定失败的线程将会阻塞,因此调度程序无法选择,直到锁定再次可用为止。

证明:
运行此代码,您将再次看到g的输出,然后再从f看到输出。

 #include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

int global=0;
sem_t sem;
void* f(void *a){
    sem_wait(&sem);
    printf("Inside a critical section: %d\n",global++);
    ++global;
    sem_post(&sem);
    return a ;
}

void* g(void *a){
    printf("Function call to g.\n");
    return a ;
}

int main(void){
    pthread_t tids[10];
    sem_init(&sem, 0, 1);
    int i;
    pthread_t new_thread;
    for (i=0; i < 10 ; ++i ){
    if ( i == 8 )
        pthread_create(&new_thread, NULL, g, NULL);
    pthread_create(&tids[i], NULL, f, NULL);
    }
    for (i=0; i < 10 ; ++i ){
    pthread_join(tids[i], NULL);
    }
    pthread_join(new_thread, NULL);
}

答案 3 :(得分:-1)

大多数情况下,调度程序可以随时切换线程或进程,即使它处于关键部分。关键部分只是代码的一个特殊部分,不应该由其他线程共享执行。这不是原子访问。只有信号量或互斥量本身就是原子访问。 如果您希望关键段代码执行是原子的,即使在SMP中也只有一个线程可以运行这些代码,您可以使用禁用中断的自旋锁。如果在处理器上禁用了中断,则调度程序停止工作,不运行。只是你的线程在关键部分运行代码,直到线程释放自旋锁。这是最高级的代码保护,仅用于内核,因为中断和内核线程之间的冲突只发生在内核中。例如,isr从设备接收数据,内核线程读取此数据。然后,spinlock工作。