系列前言
本系列是本人参加微软亚洲研究院,腾讯研究院,迅雷面试时整理的,另外也加入一些其它IT公司如百度,阿里巴巴的笔试面试题目,因此具有很强的针对性。系列中不但会详细讲解多线程同步互斥的各种“招式”,而且会进一步的讲解多线程同步互斥的“内功心法”。有了“招式”和“内功心法”,相信你也能对多线程挥洒自如,在笔试面试中顺利的秒杀多线程试题。
-------------------------------------华丽的分割线---------------------------------------
第一篇 多线程笔试面试题汇总
多线程在笔试面试中经常出现,下面列出一些公司的多线程笔试面试题。首先是一些概念性的问答题,这些是多线程的基础知识,经常出现在面试中的第一轮面试(我参加2011年腾讯研究院实习生招聘时就被问到了几个概念性题目)。然后是一些选择题,这些一般在笔试时出现,虽然不是太难,但如果在选择题上花费大多时间无疑会对后面的编程题造成影响,因此必须迅速的解决掉。最后是综合题即难一些的问答题或是编程题。这种题目当然是最难解决了,要么会引来面试官的追问,要么就很容易考虑不周全,因此解决这类题目时一定要考虑全面和细致。
下面就来看看这三类题目吧。
一.概念性问答题
第一题:线程的基本概念、线程的基本状态及状态之间的关系?
第二题:线程与进程的区别?
这个题目问到的概率相当大,计算机专业考研中也常常考到。要想全部答出比较难。
第三题:多线程有几种实现方法,都是什么?
第四题:多线程同步和互斥有几种实现方法,都是什么?
我在参加2011年迅雷校园招聘时的一面和二面都被问到这个题目,回答的好将会给面试成绩加不少分。
第五题:多线程同步和互斥有何异同,在什么情况下分别使用他们?举例说明。
二.选择题
第一题(百度笔试题):
以下多线程对int型变量x的操作,哪几个不需要进行同步:
A. x=y; B. x++; C. ++x; D. x=1;
第二题(阿里巴巴笔试题)
多线程中栈与堆是公有的还是私有的
A:栈公有, 堆私有
B:栈公有,堆公有
C:栈私有, 堆公有
D:栈私有,堆私有
三.综合题
第一题(台湾某杀毒软件公司面试题):
在Windows编程中互斥量与临界区比较类似,请分析一下二者的主要区别。
第二题:
一个全局变量tally,两个线程并发执行(代码段都是ThreadProc),问两个线程都结束后,tally取值范围。
inttally = 0;//glable
voidThreadProc()
{
for(inti = 1; i <= 50; i++)
tally += 1;
}
第三题(某培训机构的练习题):
子线程循环 10 次,接着主线程循环 100 次,接着又回到子线程循环 10 次,接着再回到主线程又循环 100 次,如此循环50次,试写出代码。
第四题(迅雷笔试题):
编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推。
第五题(Google面试题)
有四个线程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....
请设计程序。
下面的第六题与第七题也是在考研中或是程序员和软件设计师认证考试中的热门试题。
第六题
生产者消费者问题
这是一个非常经典的多线程题目,题目大意如下:有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个有多个缓冲区的缓冲池,生产者将它生产的产品放入一个缓冲区中,消费者可以从缓冲区中取走产品进行消费,所有生产者和消费者都是异步方式运行的,但它们必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个已经装满产品且尚未被取走的缓冲区中投放产品。
第七题
读者写者问题
这也是一个非常经典的多线程题目,题目大意如下:有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者读时写者也不能写。
多线程相关题目就列举到此,如果各位有多线程方面的笔试面试题,欢迎提供给我,我将及时补上。谢谢大家。
下一篇《多线程第一次亲密接触 CreateThread与_beginthreadex本质区别》将从源代码的层次上讲解创建多线程的二个函数CreateThread与_beginthreadex到底有什么区别,让你明明白白的完成与多线程第一次亲密接触。
转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/7392749
下面列出目录,方便大家查看。
1.《秒杀多线程第一篇 多线程笔试面试题汇总》
2.《秒杀多线程第二篇 多线程第一次亲密接触 CreateThread与_beginthreadex本质区别》
3.《秒杀多线程第三篇 原子操作 Interlocked系列函数》
4.《秒杀多线程第四篇 一个经典多线程同步问题》
5.《秒杀多线程第五篇 经典线程同步 关键段CS》
6.《秒杀多线程第六篇 经典线程同步 事件Event》
7.《秒杀多线程第七篇 经典线程同步 互斥量Mutex》
8.《秒杀多线程第八篇 经典线程同步 信号量Semaphore》
9.《秒杀多线程第九篇 经典线程同步总结 关键段 事件 互斥量 信号量》
10.《秒杀多线程第十篇 生产者消费者问题》
11.《秒杀多线程第十一篇 读者写者问题》
12.《秒杀多线程第十二篇 多线程同步内功心法——PV操作上》
13.《秒杀多线程第十三篇 多线程同步内功心法——PV操作下》即将发布
14.《秒杀多线程第十四篇 读者写者问题继 读写锁SRWLock》
15.《秒杀多线程第十五篇 关键段,事件,互斥量,信号量的“遗弃”问题》
16. 《秒杀多线程第十六篇 多线程十大经典案例之一 双线程读写队列数据》
再后面文章还在草稿中,就暂时不列出目录了。
问题答案:
1、基本概念
详见:线程和进程关系和区别、同步和互斥、进程间通信
2、以下多线程对int型变量x的操作,哪几个不需要进行同步(D)
A. x=y; B. x++; C. ++x; D. x=1;
详见:多线程二 多线程中的隐蔽问题揭秘
3、多线程中栈与堆是公有的还是私有的 (C)
A:栈公有, 堆私有
B:栈公有,堆公有
C:栈私有, 堆公有
D:栈私有,堆私有
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。
- #include "stdafx.h"
- #include "stdio.h"
- #include "stdlib.h"
- #include <iostream>
- #include <string>
- #include <stack>
- #include <windows.h>
- #include <process.h>
- using namespace std;
-
- const int THREAD_NUM = 10;
- HANDLE ga,gb,gc;
-
- unsigned int __stdcall FunA(void *pPM)
- {
- Sleep(50);//some work should to do
- printf("A\n");
- ReleaseSemaphore(gb, 1, NULL);//递增信号量B的资源数
-
- return 0;
- }
-
- unsigned int __stdcall FunB(void *pPM)
- {
- Sleep(50);//some work should to do
- printf("B\n");
- ReleaseSemaphore(gc, 1, NULL);//递增信号量C的资源数
-
- return 0;
- }
-
- unsigned int __stdcall FunC(void *pPM)
- {
- Sleep(50);//some work should to do
- printf("C\n");
- ReleaseSemaphore(ga, 1, NULL);//递增信号量A的资源数
-
- return 0;
- }
-
- int main()
- {
- //初始化信号量
- ga = CreateSemaphore(NULL, 1, 1, NULL);//当前1个资源,最大允许1个同时访问
- gb = CreateSemaphore(NULL, 0, 1, NULL);//当前0个资源,最大允许1个同时访问
- gc = CreateSemaphore(NULL, 0, 1, NULL);//当前0个资源,最大允许1个同时访问
-
- HANDLE handle[THREAD_NUM];
- int i = 0;
- while (i < THREAD_NUM)
- {
- WaitForSingleObject(ga, INFINITE); //等待信号量A>0
- handle[i] = (HANDLE)_beginthreadex(NULL, 0, FunA, &i, 0, NULL);
- WaitForSingleObject(gb, INFINITE); //等待信号量B>0
- handle[i] = (HANDLE)_beginthreadex(NULL, 0, FunB, &i, 0, NULL);
- WaitForSingleObject(gc, INFINITE); //等待信号量C>0
- handle[i] = (HANDLE)_beginthreadex(NULL, 0, FunC, &i, 0, NULL);
-
- ++i;
- }
- WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
-
- //销毁信号量
- CloseHandle(ga);
- CloseHandle(gb);
- CloseHandle(gc);
- for (i = 0; i < THREAD_NUM; i++)
- CloseHandle(handle[i]);
- return 0;
- }
7、生产者消费者问题:有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个有多个缓冲区的缓冲池,生产者将它生产的产品放入一个缓冲区中,消费者可以从缓冲区中取走产品进行消费,所有生产者和消费者都是异步方式运行的,但它们必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个已经装满产品且尚未被取走的缓冲区中投放产品。
分析:假设1个生产者,2个消费者,缓冲区大小为4。
第一.从缓冲区取出产品和向缓冲区投放产品必须是互斥进行的。可以用关键段和互斥量来完成。
第二.生产者要等待缓冲区为空,这样才可以投放产品,消费者要等待缓冲区不为空,这样才可以取出产品进行消费。并且由于有二个等待过程,所以要用二个事件或信号量来控制。
代码如下:
- //1生产者 2消费者 4缓冲区
- #include "stdafx.h"
- #include "stdio.h"
- #include "stdlib.h"
- #include <iostream>
- #include <string>
- #include <stack>
- #include <windows.h>
- #include <process.h>
- using namespace std;
-
- //设置控制台输出颜色
- BOOL SetConsoleColor(WORD wAttributes)
- {
- HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
- if (hConsole == INVALID_HANDLE_VALUE)
- return FALSE;
-
- return SetConsoleTextAttribute(hConsole, wAttributes);
- }
-
- const int END_PRODUCE_NUMBER = 8; //生产产品个数
- const int BUFFER_SIZE = 4; //缓冲区个数
- int g_Buffer[BUFFER_SIZE]; //缓冲池
- int g_i, g_j;
- CRITICAL_SECTION g_cs; //信号量与关键段
- HANDLE g_hSemaphoreBufferEmpty, g_hSemaphoreBufferFull;
-
- //生产者线程函数
- unsigned int __stdcall ProducerThreadFun(PVOID pM)
- {
- for (int i = 1; i <= END_PRODUCE_NUMBER; i++)
- {
- //等待有空的缓冲区出现
- WaitForSingleObject(g_hSemaphoreBufferEmpty, INFINITE);
-
- //互斥的访问缓冲区
- EnterCriticalSection(&g_cs);
- g_Buffer[g_i] = i;
- printf("生产者在缓冲池第%d个缓冲区中投放数据%d\n", g_i, g_Buffer[g_i]);
- g_i = (g_i + 1) % BUFFER_SIZE;
- LeaveCriticalSection(&g_cs);
-
- //通知消费者有新数据了
- ReleaseSemaphore(g_hSemaphoreBufferFull, 1, NULL);
- }
- printf("生产者完成任务,线程结束运行\n");
- return 0;
- }
-
- //消费者线程函数
- unsigned int __stdcall ConsumerThreadFun(PVOID pM)
- {
- while (true)
- {
- //等待非空的缓冲区出现
- WaitForSingleObject(g_hSemaphoreBufferFull, INFINITE);
-
- //互斥的访问缓冲区
- EnterCriticalSection(&g_cs);
- SetConsoleColor(FOREGROUND_GREEN);
- printf(" 编号为%d的消费者从缓冲池中第%d个缓冲区取出数据%d\n", GetCurrentThreadId(), g_j, g_Buffer[g_j]);
- SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
- if (g_Buffer[g_j] == END_PRODUCE_NUMBER)//结束标志
- {
- LeaveCriticalSection(&g_cs);
- //通知其它消费者有新数据了(结束标志)
- ReleaseSemaphore(g_hSemaphoreBufferFull, 1, NULL);
- break;
- }
- g_j = (g_j + 1) % BUFFER_SIZE;
- LeaveCriticalSection(&g_cs);
-
- Sleep(50); //some other work to do
-
- ReleaseSemaphore(g_hSemaphoreBufferEmpty, 1, NULL);
- }
- SetConsoleColor(FOREGROUND_GREEN);
- printf(" 编号为%d的消费者收到通知,线程结束运行\n", GetCurrentThreadId());
- SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
- return 0;
- }
-
- int main()
- {
- InitializeCriticalSection(&g_cs);
- //初始化信号量,一个记录有产品的缓冲区个数,另一个记录空缓冲区个数.
- g_hSemaphoreBufferEmpty = CreateSemaphore(NULL, 4, 4, NULL);
- g_hSemaphoreBufferFull = CreateSemaphore(NULL, 0, 4, NULL);
- g_i = 0;
- g_j = 0;
- memset(g_Buffer, 0, sizeof(g_Buffer));
-
- const int THREADNUM = 3;
- HANDLE hThread[THREADNUM];
- //生产者线程
- hThread[0] = (HANDLE)_beginthreadex(NULL, 0, ProducerThreadFun, NULL, 0, NULL);
- //消费者线程
- hThread[1] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun, NULL, 0, NULL);
- hThread[2] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun, NULL, 0, NULL);
- WaitForMultipleObjects(THREADNUM, hThread, TRUE, INFINITE);
-
- for (int i = 0; i < THREADNUM; i++)
- CloseHandle(hThread[i]);
-
- //销毁信号量和关键段
- CloseHandle(g_hSemaphoreBufferEmpty);
- CloseHandle(g_hSemaphoreBufferFull);
- DeleteCriticalSection(&g_cs);
- return 0;
- }
8、读者写者问题:这也是一个非常经典的多线程题目,题目大意如下:有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者读时写者也不能写。
分析:首先来找找哪些是属于“等待”情况。
第一、写者要等到没有读者时才能去写文件。
第二、所有读者要等待写者完成写文件后才能去读文件。
找完“等待”情况后,再看看有没有要互斥访问的资源。由于只有一个写者而读者们是可以共享的读文件,所以按题目要求并没有需要互斥访问的资源。代码如下:
- #include "stdafx.h"
- #include "stdio.h"
- #include "stdlib.h"
- #include <iostream>
- #include <string>
- #include <stack>
- #include <windows.h>
- #include <process.h>
- using namespace std;
-
- //读者与写者问题
- #include <stdio.h>
- #include <process.h>
- #include <windows.h>
- //设置控制台输出颜色
- BOOL SetConsoleColor(WORD wAttributes)
- {
- HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
- if (hConsole == INVALID_HANDLE_VALUE)
- return FALSE;
-
- return SetConsoleTextAttribute(hConsole, wAttributes);
- }
- const int READER_NUM = 5; //读者个数
- //关键段和事件
- CRITICAL_SECTION g_cs, g_cs_writer_count;
- HANDLE g_hEventWriter, g_hEventNoReader;
- int g_nReaderCount;
- //读者线程输出函数(变参函数的实现)
- void ReaderPrintf(char *pszFormat, ...)
- {
- va_list pArgList;
-
- va_start(pArgList, pszFormat);
- EnterCriticalSection(&g_cs);
- vfprintf(stdout, pszFormat, pArgList);
- LeaveCriticalSection(&g_cs);
- va_end(pArgList);
- }
- //读者线程函数
- unsigned int __stdcall ReaderThreadFun(PVOID pM)
- {
- ReaderPrintf(" 编号为%d的读者进入等待中...\n", GetCurrentThreadId());
- //等待写者完成
- WaitForSingleObject(g_hEventWriter, INFINITE);
-
- //读者个数增加
- EnterCriticalSection(&g_cs_writer_count);
- g_nReaderCount++;
- if (g_nReaderCount == 1)
- ResetEvent(g_hEventNoReader);
- LeaveCriticalSection(&g_cs_writer_count);
-
- //读取文件
- ReaderPrintf("编号为%d的读者开始读取文件...\n", GetCurrentThreadId());
-
- Sleep(rand() % 100);
-
- //结束阅读,读者个数减小,空位增加
- ReaderPrintf(" 编号为%d的读者结束读取文件\n", GetCurrentThreadId());
-
- //读者个数减少
- EnterCriticalSection(&g_cs_writer_count);
- g_nReaderCount--;
- if (g_nReaderCount == 0)
- SetEvent(g_hEventNoReader);
- LeaveCriticalSection(&g_cs_writer_count);
-
- return 0;
- }
- //写者线程输出函数
- void WriterPrintf(char *pszStr)
- {
- EnterCriticalSection(&g_cs);
- SetConsoleColor(FOREGROUND_GREEN);
- printf(" %s\n", pszStr);
- SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
- LeaveCriticalSection(&g_cs);
- }
- //写者线程函数
- unsigned int __stdcall WriterThreadFun(PVOID pM)
- {
- WriterPrintf("写者线程进入等待中...");
- //等待读文件的读者为零
- WaitForSingleObject(g_hEventNoReader, INFINITE);
- //标记写者正在写文件
- ResetEvent(g_hEventWriter);
-
- //写文件
- WriterPrintf(" 写者开始写文件.....");
- Sleep(rand() % 100);
- WriterPrintf(" 写者结束写文件");
-
- //标记写者结束写文件
- SetEvent(g_hEventWriter);
- return 0;
- }
-
- int main()
- {
- printf(" 读者写者问题\n");
- printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");
-
- //初始化事件和信号量
- InitializeCriticalSection(&g_cs);
- InitializeCriticalSection(&g_cs_writer_count);
-
- //手动置位,初始已触发
- g_hEventWriter = CreateEvent(NULL, TRUE, TRUE, NULL);
- g_hEventNoReader = CreateEvent(NULL, FALSE, TRUE, NULL);
- g_nReaderCount = 0;
-
- int i;
- HANDLE hThread[READER_NUM + 1];
- //先启动二个读者线程
- for (i = 1; i <= 2; i++)
- hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);
- //启动写者线程
- hThread[0] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL);
- Sleep(50);
- //最后启动其它读者结程
- for ( ; i <= READER_NUM; i++)
- hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);
- WaitForMultipleObjects(READER_NUM + 1, hThread, TRUE, INFINITE);
- for (i = 0; i < READER_NUM + 1; i++)
- CloseHandle(hThread[i]);
-
- //销毁事件和信号量
- CloseHandle(g_hEventWriter);
- CloseHandle(g_hEventNoReader);
- DeleteCriticalSection(&g_cs);
- DeleteCriticalSection(&g_cs_writer_count);
- return 0;
- }
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题,还没想好?
文章转载于:http://blog.csdn.net/morewindows/article/details/7392749