您的位置:首页 > 职场人生

关于多线程的笔试面试题

2014-07-29 13:10 330 查看
一.概念性问答题
第一题:线程的基本概念、线程的基本状态及状态之间的关系?
线程,有时称为轻量级进程,是CPU使用的基本单元;它由线程ID、程序计数器、寄存器集合和堆栈组成。它与属于同一进程的其他线程共享其代码段、数据段和其他操作系统资源(如打开文件和信号)。

线程状态:新生、就绪、运行、阻塞、终止

状态之间的转换:新生->就绪、就绪->运行,运行->阻塞,就绪->阻塞,就绪->终止,阻塞->就绪

第二题:线程与进程的区别?
参见:/article/10903399.html

第三题:多线程同步和互斥有几种实现方法,都是什么?

线程间的同步方法大体可分为两类:用户模式和内核模式。顾名思义,内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。

用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。内核模式下的方法有:事件,信号量,互斥量。

第四题:多线程同步和互斥有何异同,在什么情况下分别使用它们?

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

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

二.选择题

第一题(百度笔试题):
以下多线程对int型变量x的操作,哪几个不需要进行同步:

A.x=y; B. x++; C. ++x; D. x=1;

解:多线程操作最主要的问题是:不知道何时CPU进行线程切换,并且CPU切换是以汇编代码为准的,即在任何一行汇编代码处都有可能进行切换

在WINDOWS下 vs2010编译环境下,x=y,x++,++y都不是一行汇编代码所能完成的,x=1为一行汇编,所以需要进行同步

而在linux下,gcc编译环境下,x=y不是一行,而x++,++x,x=1都是一行

综上,应该选D

第二题(阿里巴巴笔试题)
多线程中的栈与堆是公有还是私有的?

A:栈公有, 堆私有
B:栈公有,堆公有
C:栈私有, 堆公有
D:栈私有,堆私有
解:进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本,程序的全局内存和堆内存、栈以及文件描述符。

三.综合题
第一题(台湾某杀毒软件公司面试题):
在Windows编程中互斥量与临界区比较类似,请分析一下二者的主要区别。
临界区不能用于进程间同步, 它不属于内核对象, 所以速度比较快

互斥量可以用于进程间同步, 属于内核对象, 速度较慢

windows下的,这个不是很懂

第二题:

一个全局变量tally,两个线程并发执行(代码段都是ThreadProc),问两个线程都结束后,tally取值范围。
int tally = 0;//glable
voidThreadProc()
{
for(int i = 1; i <= 50; i++)
tally += 1;
}

50~100
解:假设有点个线程A,B;那么什么时候tally的取值最大呢,什么时候tally的取值最小呢?

首先从题意中我们可以认为这两个线程不会意外退出,所以肯定是正常执行完毕。

因此,取最大值的时候:即A,B两个线程依次进行,A执行完,B再接着执行,tally = 50 + 50 = 100

去最小值时:即每次A执行完tally += 1后,立马切换到线程B,等于B中对tally值的修改把A中对tally的操作覆盖了。

可以用代码实验一下

<span style="font-size:14px;"> 1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<pthread.h>
4
5 int tally = 0;  //全局变量
6 void *thread_proc(void *arg)
7 {
8     for(int i = 1;i <= 500000000;i++)
9         tally += 1;
10 }
11
12 int main()
13 {
14     pthread_t id1,id2;
15     void *tret;
16
17     pthread_create(&id1,NULL,thread_proc,NULL);
18     pthread_create(&id2,NULL,thread_proc,NULL);
19
20     pthread_join(id1,&tret);
21     printf("线程1退出!\n");
22     pthread_join(id2,&tret);
23     printf("线程2退出!\n");
24     printf("%d\n",tally);
25     return 0;
26 }
</span>
正常情况下应该输出1000000000,但是实际上是输出853581651.至于为什么循环这么多次,因为次数少的话还没等线程切换,先创建的线程就已经执行结束了,不能看出效果。

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

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>

pthread_mutex_t flag = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_main = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_child = PTHREAD_COND_INITIALIZER;

void *child_thread(void *arg)
{
int child = 0;
while(1){
pthread_mutex_lock(&flag);
pthread_cond_wait(&cond_child,&flag);
if(child == 50){
pthread_mutex_unlock(&flag);
pthread_cond_signal(&cond_main);
break;
}
for(int i = 0;i < 10;i++)
;
printf("子线程循环10次,第%d遍!\n",child + 1);
child++;
pthread_mutex_unlock(&flag);
sleep(1);
pthread_cond_signal(&cond_main);
}
return NULL;
}

int main()
{
pthread_t child_id;
int main = 0;
void *tert;

int err = pthread_create(&child_id,NULL,child_thread,NULL);

sleep(1);   //让主线程休眠,切换到子线程,使子线程阻塞
pthread_cond_signal(&cond_child);
while(1){
pthread_mutex_lock(&flag);
pthread_cond_wait(&cond_main,&flag);
if(main == 50){
pthread_mutex_unlock(&flag);
pthread_cond_signal(&cond_child);
break;
}
for(int i = 0;i < 100;i++)
;
printf("主线程已循环100次,第%d遍!\n",main + 1);
main++;
pthread_mutex_unlock(&flag);
sleep(1);
pthread_cond_signal(&cond_child);
}
pthread_join(child_id,&tert);
printf("子线程结束\n");
printf("主线程结束\n");
return 0;
}


第四题:

编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推。
利用互斥量和条件变量即可实现。代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>

int count = 0; //设定输出循环次数
pthread_mutex_t a_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  a_cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t  b_cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t  c_cond = PTHREAD_COND_INITIALIZER;
void *thread_A(void *arg)
{
int a = 0;
while(1){
pthread_mutex_lock(&a_lock);
pthread_cond_wait(&a_cond,&a_lock);
if(a == 10){
pthread_mutex_unlock(&a_lock);
pthread_cond_signal(&b_cond);
break;
}
a++;
printf("A");
pthread_mutex_unlock(&a_lock);
sleep(1);
pthread_cond_signal(&b_cond);
}
return NULL;
}

void *thread_B(void *arg)
{
int b = 0;
while(1){
pthread_mutex_lock(&a_lock);
pthread_cond_wait(&b_cond,&a_lock);
if(b == 10){
pthread_mutex_unlock(&a_lock);
pthread_cond_signal(&c_cond);
break;
}
b++;
printf("B");
pthread_mutex_unlock(&a_lock);
sleep(1);
pthread_cond_signal(&c_cond);

}
return NULL;
}

void *thread_C(void *arg)
{
int c = 0;
while(1){
pthread_mutex_lock(&a_lock);
pthread_cond_wait(&c_cond,&a_lock);
if(c == 10)
break;
c++;
printf("C\n");
pthread_mutex_unlock(&a_lock);
sleep(1);
pthread_cond_signal(&a_cond);
}
return NULL;
}

int main()
{
pthread_t Aid,Bid,Cid;
void *tret;

pthread_create(&Aid,NULL,thread_A,NULL);
pthread_create(&Bid,NULL,thread_B,NULL);
pthread_create(&Cid,NULL,thread_C,NULL);

sleep(5);  //主线程休眠,使ABC三个线程达到阻塞状态
pthread_cond_signal(&a_cond);   //发送信号,激活A线程

pthread_join(Aid,&tret);
printf("线程A结束!\n");
pthread_join(Bid,&tret);
printf("线程B结束!\n");
pthread_join(Cid,&tret);
printf("线程C结束!\n");

return 0;
}


第五题:

有四个线程1、2、3、4。线程1的功能就是输出1,线程2的功能就是输出2,以此类推.........现在有四个文件ABCD。初始都为空。现要让四个文件呈如下格式:
分析:原理同第四题,只是将线程发送条件变量的顺序调换。。。(应该是这样,莫非还有其他的限制?)
第六题:

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

分析:同样适用互斥量和条件变量

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

typedef struct elem{
int flag; //缓冲区是否使用的标志
int date;
}Elem;

int count = 0; //记录可用缓冲的大小
Elem buff[100];
pthread_mutex_t flag = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t use_cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t unfull_cond = PTHREAD_COND_INITIALIZER;

void* pruducer(void *arg)
{
while(1){
pthread_mutex_lock(&flag);
if(count > 90)
pthread_cond_wait(&unfull_cond,&flag);
for(int i = 0;i < 1000;i++){
if(buff[i].flag == 0){
printf("生产者向缓冲池%d中,投入一个产品%d\n",i,i);
buff[i].flag = 1;
buff[i].date = i;
count++;
break;
}
}
sleep(1);
pthread_mutex_unlock(&flag);
pthread_cond_signal(&use_cond);
}
}

void* consumer(void *arg)
{
while(1){
pthread_mutex_lock(&flag);
if(count < 10)
pthread_cond_wait(&use_cond,&flag);
for(int i = 0;i < 1000;i++)
if(buff[i].flag == 1){
printf("消费者从缓冲池%d中,消费一个产品%d\n",i,buff[i].date);
buff[i].flag = 0;
buff[i].date = -1;
count--;
break;
}
sleep(1);
pthread_mutex_unlock(&flag);
pthread_cond_signal(&unfull_cond);
}
}

int main()
{
pthread_t p_id,c_id;
void* tret;
//初始化缓冲区
for(int i = 0;i < 10;i++){
buff[i].flag = 0;
buff[i].date = -1;
}

pthread_create(&p_id,NULL,pruducer,NULL);
sleep(1);
pthread_create(&c_id,NULL,consumer,NULL);

pthread_join(p_id,&tret);
pthread_join(c_id,&tret);
return 0;
}


第七题:读写者问题

读者写者问题
题目大意如下:有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者读时写者也不能写。
分析:原理和第六题类似,生产者相对写者,消费者相对读者,此外多个读者,对应多个读线程,不过linux下给我提供了更好的解决方法——读写锁,下面我们就用读写锁解决这个问题
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<signal.h>

pthread_rwlock_t rw_lock;  //读写锁
int buff;  //读写缓冲区
int flag = 0;  //线程结束标志
void *read_thread(void *arg)
{
while(1){
pthread_rwlock_rdlock(&rw_lock); //读模式加锁
printf("读线程 %d 开始读取,值为:%d\n",(int)pthread_self(),buff);
if(flag == 1){
pthread_rwlock_unlock(&rw_lock);
break;
}
pthread_rwlock_unlock(&rw_lock);
sleep(1);
}
return NULL;
}

void *write_thread(void *arg)
{
while(1){
pthread_rwlock_wrlock(&rw_lock);  //写模式加锁
if(flag == 1){
pthread_rwlock_unlock(&rw_lock);
break;
}
int temp = rand();
buff = temp;
printf("写线程 %d 开始写入,buff值为:%d\n",(int)pthread_self(),temp);
pthread_rwlock_unlock(&rw_lock);
sleep(2);
}
return NULL;
}

//获取中断信号,设置线程退出标志
static void quit_thread(int signo){
pthread_rwlock_wrlock(&rw_lock);
flag = 1;
printf("线程准备退出\n");
pthread_rwlock_unlock(&rw_lock);
}
int main()
{
pthread_t r_id1,r_id2,w_id;
void* tret;
signal(SIGINT,quit_thread);
pthread_rwlock_init(&rw_lock,NULL);  //初始化读写锁

//创建三个线程,一个写线程,两个读线程
pthread_create(&w_id,NULL,write_thread,NULL);
sleep(1);
pthread_create(&r_id1,NULL,read_thread,NULL);
pthread_create(&r_id2,NULL,read_thread,NULL);

pthread_join(w_id,&tret);
printf("写线程结束\n");
pthread_join(r_id1,&tret);
printf("读线程结束\n");
pthread_join(r_id2,&tret);
printf("读线程结束\n");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: