多线程经典面试题

Posted qiuri2008

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程经典面试题相关的知识,希望对你有一定的参考价值。

1、基本概念

        详见:线程和进程关系和区别、同步和互斥、进程间通信

2、以下多线程对int型变量x的操作,哪几个不需要进行同步(D)
        A. x=y;      B. x++;    C. ++x;    D. x=1;

        详见:多线程二 多线程中的隐蔽问题揭秘

3、多线程中栈与堆

1、基本概念

        详见:线程和进程关系和区别、同步和互斥、进程间通信

2、以下多线程对int型变量x的操作,哪几个不需要进行同步(D)
        A. x=y;      B. x++;    C. ++x;    D. x=1;

        详见:多线程二 多线程中的隐蔽问题揭秘

3、多线程中栈与堆是公有的还是私有的 (C)

        A:栈公有, 堆私有

        B:栈公有,堆公有

        C:栈私有, 堆公有

        D:栈私有,堆私有

-----------------------------------------------------------------------------------------------------

1、栈区(stack)― 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 
2、堆区(heap) ― 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 
3、全局区(静态区)(static)―,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 
4、文字常量区 ―常量字符串就是放在这里的。 程序结束后由系统释放 
5、程序代码区―存放函数体的二进制代码。 
--------------------------------------------------------------

 

4、临界区(Critical Section)和互斥量(Mutex)

        两者都可以用于同一进程中不同子线程对资源的互斥访问。

        互斥量是内核对象,因此还可以用于不同进程中子线程对资源的互斥访问。

        互斥量可以很好的解决由于线程意外终止资源无法释放的问题。

5、一个全局变量tally,两个线程并发执行(代码段都是ThreadProc),问两个线程都结束后,tally取值范围。

        inttally = 0;//glable

        voidThreadProc()

        {

              for(inti = 1;i <= 50;i++)

                   tally += 1;

        }

        答:[50,100]

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

        思路:用信号量进行各个子线程之间的互斥,创建3个信号量A、B、C。初始时A的资源数为1,B、C的资源数为0,访问A之后,将B的资源数加1,访问B之后将C的资源数加1,访问C之后将A的资源数加1。创建3个子线程顺序访问资源A、B、C。

[html] view plaincopy
 
  1. #include "stdafx.h"  
  2. #include "stdio.h"  
  3. #include "stdlib.h"  
  4. #include <iostream>  
  5. #include <string>  
  6. #include <stack>  
  7. #include <windows.h>  
  8. #include <process.h>  
  9. using namespace std;  
  10.   
  11. const int THREAD_NUM = 10;  
  12. HANDLE            ga,gb,gc;  
  13.   
  14. unsigned int __stdcall FunA(void *pPM)  
  15. {  
  16.     Sleep(50);//some work should to do  
  17.     printf("A\n");  
  18.     ReleaseSemaphore(gb, 1, NULL);//递增信号量B的资源数    
  19.   
  20.     return 0;  
  21. }  
  22.   
  23. unsigned int __stdcall FunB(void *pPM)  
  24. {     
  25.     Sleep(50);//some work should to do  
  26.     printf("B\n");  
  27.     ReleaseSemaphore(gc, 1, NULL);//递增信号量C的资源数    
  28.   
  29.     return 0;  
  30. }  
  31.   
  32. unsigned int __stdcall FunC(void *pPM)  
  33. {  
  34.     Sleep(50);//some work should to do  
  35.     printf("C\n");  
  36.     ReleaseSemaphore(ga, 1, NULL);//递增信号量A的资源数    
  37.   
  38.     return 0;  
  39. }  
  40.   
  41. int main()  
  42. {  
  43.     //初始化信号量  
  44.     ga = CreateSemaphore(NULL, 1, 1, NULL);//当前1个资源,最大允许1个同时访问  
  45.     gb = CreateSemaphore(NULL, 0, 1, NULL);//当前0个资源,最大允许1个同时访问  
  46.     gc = CreateSemaphore(NULL, 0, 1, NULL);//当前0个资源,最大允许1个同时访问  
  47.       
  48.     HANDLE  handle[THREAD_NUM];   
  49.     int i = 0;  
  50.     while (i < THREAD_NUM)   
  51.     {  
  52.         WaitForSingleObject(ga, INFINITE);  //等待信号量A>0  
  53.         handle[i] = (HANDLE)_beginthreadex(NULL, 0, FunA, &i, 0, NULL);  
  54.         WaitForSingleObject(gb, INFINITE);  //等待信号量B>0  
  55.         handle[i] = (HANDLE)_beginthreadex(NULL, 0, FunB, &i, 0, NULL);  
  56.         WaitForSingleObject(gc, INFINITE);  //等待信号量C>0  
  57.         handle[i] = (HANDLE)_beginthreadex(NULL, 0, FunC, &i, 0, NULL);  
  58.           
  59.         ++i;  
  60.     }  
  61.     WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);  
  62.       
  63.     //销毁信号量  
  64.     CloseHandle(ga);  
  65.     CloseHandle(gb);  
  66.     CloseHandle(gc);  
  67.     for (i = 0; i < THREAD_NUM; i++)  
  68.         CloseHandle(handle[i]);  
  69.     return 0;  
  70. }  

7、生产者消费者问题:有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个有多个缓冲区的缓冲池,生产者将它生产的产品放入一个缓冲区中,消费者可以从缓冲区中取走产品进行消费,所有生产者和消费者都是异步方式运行的,但它们必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个已经装满产品且尚未被取走的缓冲区中投放产品。

        分析:假设1个生产者,2个消费者,缓冲区大小为4。

第一.从缓冲区取出产品和向缓冲区投放产品必须是互斥进行的。可以用关键段和互斥量来完成。

第二.生产者要等待缓冲区为空,这样才可以投放产品,消费者要等待缓冲区不为空,这样才可以取出产品进行消费。并且由于有二个等待过程,所以要用二个事件或信号量来控制。

代码如下:

[html] view plaincopy
 
  1. //1生产者 2消费者 4缓冲区  
  2. #include "stdafx.h"  
  3. #include "stdio.h"  
  4. #include "stdlib.h"  
  5. #include <iostream>  
  6. #include <string>  
  7. #include <stack>  
  8. #include <windows.h>  
  9. #include <process.h>  
  10. using namespace std;  
  11.   
  12. //设置控制台输出颜色  
  13. BOOL SetConsoleColor(WORD wAttributes)  
  14. {  
  15.     HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);  
  16.     if (hConsole == INVALID_HANDLE_VALUE)  
  17.         return FALSE;  
  18.       
  19.     return SetConsoleTextAttribute(hConsole, wAttributes);  
  20. }  
  21.   
  22. const int END_PRODUCE_NUMBER = 8;   //生产产品个数  
  23. const int BUFFER_SIZE = 4;          //缓冲区个数  
  24. int g_Buffer[BUFFER_SIZE];          //缓冲池  
  25. int g_i, g_j;  
  26. CRITICAL_SECTION g_cs;              //信号量与关键段  
  27. HANDLE g_hSemaphoreBufferEmpty, g_hSemaphoreBufferFull;  
  28.   
  29. //生产者线程函数  
  30. unsigned int __stdcall ProducerThreadFun(PVOID pM)  
  31. {  
  32.     for (int i = 1; i <= END_PRODUCE_NUMBER; i++)  
  33.     {  
  34.         //等待有空的缓冲区出现  
  35.         WaitForSingleObject(g_hSemaphoreBufferEmpty, INFINITE);  
  36.   
  37.         //互斥的访问缓冲区  
  38.         EnterCriticalSection(&g_cs);  
  39.         g_Buffer[g_i] = i;  
  40.         printf("生产者在缓冲池第%d个缓冲区中投放数据%d\n", g_i, g_Buffer[g_i]);  
  41.         g_i = (g_i + 1) % BUFFER_SIZE;  
  42.         LeaveCriticalSection(&g_cs);  
  43.   
  44.         //通知消费者有新数据了  
  45.         ReleaseSemaphore(g_hSemaphoreBufferFull, 1, NULL);  
  46.     }  
  47.     printf("生产者完成任务,线程结束运行\n");  
  48.     return 0;  
  49. }  
  50.   
  51. //消费者线程函数  
  52. unsigned int __stdcall ConsumerThreadFun(PVOID pM)  
  53. {  
  54.     while (true)  
  55.     {  
  56.         //等待非空的缓冲区出现  
  57.         WaitForSingleObject(g_hSemaphoreBufferFull, INFINITE);  
  58.           
  59.         //互斥的访问缓冲区  
  60.         EnterCriticalSection(&g_cs);  
  61.         SetConsoleColor(FOREGROUND_GREEN);  
  62.         printf("  编号为%d的消费者从缓冲池中第%d个缓冲区取出数据%d\n", GetCurrentThreadId(), g_j, g_Buffer[g_j]);  
  63.         SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);  
  64.         if (g_Buffer[g_j] == END_PRODUCE_NUMBER)//结束标志  
  65.         {  
  66.             LeaveCriticalSection(&g_cs);  
  67.             //通知其它消费者有新数据了(结束标志)  
  68.             ReleaseSemaphore(g_hSemaphoreBufferFull, 1, NULL);  
  69.             break;  
  70.         }  
  71.         g_j = (g_j + 1) % BUFFER_SIZE;  
  72.         LeaveCriticalSection(&g_cs);  
  73.   
  74.         Sleep(50); //some other work to do  
  75.   
  76.         ReleaseSemaphore(g_hSemaphoreBufferEmpty, 1, NULL);  
  77.     }  
  78.     SetConsoleColor(FOREGROUND_GREEN);  
  79.     printf("  编号为%d的消费者收到通知,线程结束运行\n", GetCurrentThreadId());  
  80.     SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);  
  81.     return 0;  
  82. }  
  83.   
  84. int main()  
  85. {  
  86.     InitializeCriticalSection(&g_cs);  
  87.     //初始化信号量,一个记录有产品的缓冲区个数,另一个记录空缓冲区个数.  
  88.     g_hSemaphoreBufferEmpty = CreateSemaphore(NULL, 4, 4, NULL);  
  89.     g_hSemaphoreBufferFull  = CreateSemaphore(NULL, 0, 4, NULL);  
  90.     g_i = 0;  
  91.     g_j = 0;  
  92.     memset(g_Buffer, 0, sizeof(g_Buffer));  
  93.   
  94.     const int THREADNUM = 3;  
  95.     HANDLE hThread[THREADNUM];  
  96.     //生产者线程  
  97.     hThread[0] = (HANDLE)_beginthreadex(NULL, 0, ProducerThreadFun, NULL, 0, NULL);  
  98.     //消费者线程  
  99.     hThread[1] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun, NULL, 0, NULL);  
  100.     hThread[2] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun, NULL, 0, NULL);  
  101.     WaitForMultipleObjects(THREADNUM, hThread, TRUE, INFINITE);  
  102.   
  103.     for (int i = 0; i < THREADNUM; i++)        
  104.         CloseHandle(hThread[i]);  
  105.   
  106.     //销毁信号量和关键段  
  107.     CloseHandle(g_hSemaphoreBufferEmpty);  
  108.     CloseHandle(g_hSemaphoreBufferFull);  
  109.     DeleteCriticalSection(&g_cs);  
  110.     return 0;  
  111. }  

8、读者写者问题:这也是一个非常经典的多线程题目,题目大意如下:有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者读时写者也不能写。

    分析:首先来找找哪些是属于“等待”情况。

第一、写者要等到没有读者时才能去写文件。

第二、所有读者要等待写者完成写文件后才能去读文件。

找完“等待”情况后,再看看有没有要互斥访问的资源。由于只有一个写者而读者们是可以共享的读文件,所以按题目要求并没有需要互斥访问的资源。代码如下:

[html] view plaincopy
 
  1. #include "stdafx.h"  
  2. #include "stdio.h"  
  3. #include "stdlib.h"  
  4. #include <iostream>  
  5. #include <string>  
  6. #include <stack>  
  7. #include <windows.h>  
  8. #include <process.h>  
  9. using namespace std;  
  10.   
  11. //读者与写者问题  
  12. #include <stdio.h>  
  13. #include <process.h>  
  14. #include <windows.h>  
  15. //设置控制台输出颜色  
  16. BOOL SetConsoleColor(WORD wAttributes)  
  17. {  
  18.     HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);  
  19.     if (hConsole == INVALID_HANDLE_VALUE)  
  20.         return FALSE;  
  21.       
  22.     return SetConsoleTextAttribute(hConsole, wAttributes);  
  23. }  
  24. const int READER_NUM = 5;  //读者个数  
  25. //关键段和事件  
  26. CRITICAL_SECTION g_cs, g_cs_writer_count;  
  27. HANDLE g_hEventWriter, g_hEventNoReader;  
  28. int g_nReaderCount;  
  29. //读者线程输出函数(变参函数的实现)  
  30. void ReaderPrintf(char *pszFormat, ...)  
  31. {  
  32.     va_list   pArgList;  
  33.       
  34.     va_start(pArgList, pszFormat);  
  35.     EnterCriticalSection(&g_cs);  
  36.     vfprintf(stdout, pszFormat, pArgList);  
  37.     LeaveCriticalSection(&g_cs);  
  38.     va_end(pArgList);  
  39. }  
  40. //读者线程函数  
  41. unsigned int __stdcall ReaderThreadFun(PVOID pM)  
  42. {  
  43.     ReaderPrintf("     编号为%d的读者进入等待中...\n", GetCurrentThreadId());  
  44.     //等待写者完成  
  45.     WaitForSingleObject(g_hEventWriter, INFINITE);  
  46.   
  47.     //读者个数增加  
  48.     EnterCriticalSection(&g_cs_writer_count);  
  49.     g_nReaderCount++;  
  50.     if (g_nReaderCount == 1)  
  51.         ResetEvent(g_hEventNoReader);  
  52.     LeaveCriticalSection(&g_cs_writer_count);  
  53.   
  54.     //读取文件  
  55.     ReaderPrintf("编号为%d的读者开始读取文件...\n", GetCurrentThreadId());  
  56.   
  57.     Sleep(rand() % 100);  
  58.   
  59.     //结束阅读,读者个数减小,空位增加  
  60.     ReaderPrintf(" 编号为%d的读者结束读取文件\n", GetCurrentThreadId());  
  61.   
  62.     //读者个数减少  
  63.     EnterCriticalSection(&g_cs_writer_count);  
  64.     g_nReaderCount--;  
  65.     if (g_nReaderCount == 0)  
  66.         SetEvent(g_hEventNoReader);  
  67.     LeaveCriticalSection(&g_cs_writer_count);  
  68.   
  69.     return 0;  
  70. }  
  71. //写者线程输出函数  
  72. void WriterPrintf(char *pszStr)  
  73. {  
  74.     EnterCriticalSection(&g_cs);  
  75.     SetConsoleColor(FOREGROUND_GREEN);  
  76.     printf("     %s\n", pszStr);  
  77.     SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);  
  78.     LeaveCriticalSection(&g_cs);  
  79. }  
  80. //写者线程函数  
  81. unsigned int __stdcall WriterThreadFun(PVOID pM)  
  82. {  
  83.     WriterPrintf("写者线程进入等待中...");  
  84.     //等待读文件的读者为零  
  85.     WaitForSingleObject(g_hEventNoReader, INFINITE);  
  86.     //标记写者正在写文件  
  87.     ResetEvent(g_hEventWriter);  
  88.           
  89.     //写文件  
  90.     WriterPrintf("  写者开始写文件.....");  
  91.     Sleep(rand() % 100);  
  92.     WriterPrintf("  写者结束写文件");  
  93.   
  94.     //标记写者结束写文件  
  95.     SetEvent(g_hEventWriter);  
  96.     return 0;  
  97. }  
  98.   
  99. int main()  
  100. {  
  101.     printf("  读者写者问题\n");  
  102.     printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");  
  103.   
  104.     //初始化事件和信号量  
  105.     InitializeCriticalSection(&g_cs);  
  106.     InitializeCriticalSection(&g_cs_writer_count);  
  107.   
  108.     //手动置位,初始已触发  
  109.     g_hEventWriter = CreateEvent(NULL, TRUE, TRUE, NULL);  
  110.     g_hEventNoReader  = CreateEvent(NULL, FALSE, TRUE, NULL);  
  111.     g_nReaderCount = 0;  
  112.   
  113.     int i;  
  114.     HANDLE hThread[READER_NUM + 1];  
  115.     //先启动二个读者线程  
  116.     for (i = 1; i <= 2; i++)  
  117.         hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);  
  118.     //启动写者线程  
  119.     hThread[0] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL);  
  120.     Sleep(50);  
  121.     //最后启动其它读者结程  
  122.     for ( ; i <= READER_NUM; i++)  
  123.         hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);  
  124.     WaitForMultipleObjects(READER_NUM + 1, hThread, TRUE, INFINITE);  
  125.     for (i = 0; i < READER_NUM + 1; i++)  
  126.         CloseHandle(hThread[i]);  
  127.   
  128.     //销毁事件和信号量  
  129.     CloseHandle(g_hEventWriter);  
  130.     CloseHandle(g_hEventNoReader);  
  131.     DeleteCriticalSection(&g_cs);  
  132.     DeleteCriticalSection(&g_cs_writer_count);  
  133.     return 0;  
  134. }  

9、有四个线程1、2、3、4。线程1的功能就是输出1,线程2的功能就是输出2,以此类推.........现在有四个文件ABCD。初始都为空。现要让四个文件呈如下格式:

A:1 2 3 4 1 2....

B:2 3 4 1 2 3....

C:3 4 1 2 3 4....

D:4 1 2 3 4 1....

请设计程序。

         参考第6题,还没想好?

以上是关于多线程经典面试题的主要内容,如果未能解决你的问题,请参考以下文章

多线程经典笔试面试题

最全多线程经典面试题和答案

最全多线程经典面试题和答案

多线程面试题系列(16):多线程十大经典案例之一 双线程读写队列数据

Java经典面试题汇总多线程

多线程面试题系列:经典线程同步 关键段CS