多线程面试秒杀系列1---面试题目

来源:互联网 发布:mysql的日期和时间函数 编辑:程序博客网 时间:2024/06/06 04:00
大体结构来自morewinodws的博客。


简答题
1.线程的基本概念、线程的基本状态及状态之间的关系?
        线程是进程中某个单一顺序的控制流,是程序执行流的最小单位。线程由线程ID、当前指令指针、寄存器集合和堆栈组成。线程是进程的一个实体,是被系统调度和分配的基本单位,线程与同一进程中的其他线程共享进程的全部资源。
        一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
        在多线程OS中,通常是在一个进程中包括多个线程,每个线程都是作为利用CPU的基本单位,是花费最小开销的实体。线程具有以下属性。

        1)轻型实体       

        线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源,比如,在每个线程中都应具有一个用于控制线程运行的线程控制块TCB,用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器和堆栈。

        2)独立调度和分派的基本单位。       

        在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小。

        3)可并发执行。       

        在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行。

        4)共享进程资源。       

        在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的地址空间(进程的地址空间),这意味着,线程可以访问该地址空间的每一个虚地址;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。

2.线程与进程的区别?
        (1)进程是系统资源分配和调度的独立单位;线程是进程的一个实体,是CPU调度和分派的基本单位。     
        (2)系统资源分配给进程;线程与资源分配无关,线程与同一进程内的其他线程共享进程的全部资源。
        (3)不同的进程拥有不同的虚拟地址;同一进程下的不同线程共享同一地址空间。
       
        线程和进程的区别在于,子进程和父进程有相同的代码段,不同的数据段,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文。多线程主要是为了节约CPU时间,发挥利用,根据具体情况而定。线程的运行中需要使用计算机的内存资源和CPU。通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度,从而显著提高系统资源的利用率和吞吐量。

        系统引入线程的好处:       

        1)创建一个新线程花费的时间少。      

        2)两个线程(在同一进程中的)的切换时间少。     

        3)由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。       

        4)线程能独立执行,能充分利用和发挥处理机与外围设备并行工作的能力。


3.C++多线程有几种实现方法,都是什么?

       多线程       

       1. 继承 Thread 类       

       2. 实现 Runnable 接口再 new Thread(YourRunnableOjbect)

       线程同步       

       1. 用 synchronized 修饰需要同步的方法       

       2. 用 synchronized 块包围需要同步的语句       

       3. 使用 java.util.concurrent 包中的各种同步锁


4.多线程同步和互斥有几种实现方法,都是什么?


       内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。       

       实现方法分为用户模式和内核模式。用户模式:临界区;内核模式:互斥量,信号量,事件。


       临界区(Critical Section)      

       保证在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。临界区包含两个操作原语:


       EnterCriticalSection()进入临界区

       LeaveCriticalSection()离开临界区

       EnterCriticalSection()语句执行后代码将进入临界区以后无论发生什么,必须确保与之匹配的

       LeaveCriticalSection()都能够被执行到。否则临界区保护的共享资源将永远不会被释放。虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。


       事件(Event)      

       事件对象也可以通过通知操作的方式来保持线程的同步。并且可以实现不同进程中的线程同步操作。

       互斥量(Mutex)      

       互斥量跟临界区很相似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。互斥量比临界区复杂。因为使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。

       信号量(Semaphores)      

       信号量对象对线程的同步方式与前面几种方法不同,信号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。它指出了同时访问共享资源的线程最大数目。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。



5.多线程同步和互斥有何异同,在什么情况下分别使用他们?举例说明。

       线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。     

       线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。      

       线程同步一般是当多个线程相互协作,存在相互依赖的关系;线程互斥是包括临界资源等的访问,相互线程之间是互斥访问。




选择题

1.以下多线程对int型变量x的操作,哪几个不需要进行同步:
       A. x=y;      B. x++;    C. ++x;    D. x=1;
2.多线程中栈与堆是公有的还是私有的
      A:栈公有,堆私有      B:栈公有,堆公有      C:栈私有,堆公有      D:栈私有,堆私有


编程题

1.子线程循环 10 次,接着主线程循环 100 次,接着又回到子线程循环 10 次,接着再回到主线程又循环 100 次,如此循环50次,试写出代码。


<span style="font-size:14px;">#include<stdio.h>#include<stdlib.h>#include<errno.h>#include<pthread.h>#include<unistd.h> int num=1;  //全局变量 static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;static pthread_cond_t cond=PTHREAD_COND_INITIALIZER; void *func(); int main(){    pthread_t tid;    int ret=0,i,j;    if((ret=pthread_create(&tid,NULL,func,NULL))!=0)        printf("create thread error!\n");    for(i=0;i<3;i++)    {         pthread_mutex_lock(&mutex);        if(num!=0)            pthread_cond_wait(&cond,&mutex);        num=1;        for(j=0;j<10;j++)        {            printf("0");        }        printf("\n");        pthre2.编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推。ad_mutex_unlock(&mutex);        pthread_cond_signal(&cond);    }    return 0;} void *func(){    int i,j;    for(i=0;i<3;i++)    {        pthread_mutex_lock(&mutex);        if(num!=1)            pthread_cond_wait(&cond,&mutex);        num=0;        for(j=0;j<5;j++)        {            printf("1");        }        printf("\n");        pthread_mutex_unlock(&mutex);        pthread_cond_signal(&cond);    }    pthread_exit(0);}</span>



2.编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推。


<span style="font-size:14px;">#include<stdio.h>#include<stdlib.h>#include<error.h>#include<unistd.h>#include<pthread.h> int num=0; static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;static pthread_cond_t cond=PTHREAD_COND_INITIALIZER; void *func(void *); int main(){    pthread_t tid[3];    int ret=0,i;    for(i=0;i<3;i++)        if((ret=pthread_create(&tid[i],NULL,func,(void*)i))!=0)            printf("create thread_%c error\n",i+'A');    for(i=0;i<3;i++)        pthread_join(tid[i],NULL);    printf("\n");    return 0;} void *func(void *argc){    int i;    for(i=0;i<10;i++)    {        pthread_mutex_lock(&mutex);        while(num!=(int)argc)            pthread_cond_wait(&cond,&mutex);        printf("%c",num+'A');        num=(num+1)%3;        pthread_mutex_unlock(&mutex);        pthread_cond_broadcast(&cond);    }    pthread_exit(0);}</span>



3. 有一int型全局变量g_Flag初始值为0;在主线称中起动线程1,打印“this is thread1”,并将g_Flag设置为1 在主线称中启动线程2,打印“this is thread2”,并将g_Flag设置为2 线程序1需要在线程2退出后才能退出主线程在检测到g_Flag从1变为2,或者从2变为1的时候退出    


 
<span style="font-size:14px;">#include<stdio.h>#include<stdlib.h>#include<error.h>#include<pthread.h>#include<unistd.h> int flag=0; void *thread1(void*);void *thread2(void*); static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;static pthread_cond_t cond=PTHREAD_COND_INITIALIZER; int main(){    printf("enter main\n");    pthread_t tid1,tid2;    int ret=0;    if((ret=pthread_create(&tid1,NULL,thread1,&tid2))!=0)        printf("create thread1 error!\n");    if((ret=pthread_create(&tid2,NULL,thread2,NULL))!=0)        printf("create thread2 error!\n");    pthread_cond_wait(&cond,&mutex);    printf("leave main\n");    return 0;} void *thread1(void *argc){    printf("this is thread1\n");    pthread_mutex_lock(&mutex);    if(flag==2)        pthread_cond_signal(&cond);    flag=1;    pthread_mutex_unlock(&mutex);    pthread_join(*(pthread_t*)argc,NULL);    pthread_exit(0);} void *thread2(void *argc){    printf("this is thread2\n");    pthread_mutex_lock(&mutex);    if(flag==1)        pthread_cond_signal(&cond);    flag=2;    pthread_mutex_unlock(&mutex);    pthread_exit(0);}</span>



0 0