龙空技术网

几个经典的linux c多线程面试题解析

linux技术栈 717

前言:

当前各位老铁们对“c语言多进程多线程面试题”可能比较看重,同学们都需要知道一些“c语言多进程多线程面试题”的相关文章。那么小编同时在网摘上汇集了一些对于“c语言多进程多线程面试题””的相关文章,希望你们能喜欢,各位老铁们一起来学习一下吧!

一、线程与进程的区别?

1、 线程是进程的一部分,所以线程有的时候被称为是轻权进程或者轻量级进程。

2、 一个没有线程的进程是可以被看作单线程的,如果一个进程内拥有多个进程,进程的执行过程不是一条线(线程)的,而是多条线(线程)共同完成的。

3、 系统在运行的时候会为每个进程分配不同的内存区域,但是不会为线程分配内存(线程所使用的资源是它所属的进程的资源),线程组只能共享资源。那就是说,出了CPU之外(线程在运行的时候要占用CPU资源),计算机内部的软硬件资源的分配与线程无关,线程只能共享它所属进程的资源。

4、 与进程的控制表PCB相似,线程也有自己的控制表TCB,但是TCB中所保存的线程状态比PCB表中少多了。

5、 进程是系统所有资源分配时候的一个基本单位,拥有一个完整的虚拟空间地址,并不依赖线程而独立存在。

二、多线程有几种实现方法,都是什么?

1. 继承 Thread 类

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

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

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

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

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

#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <string.h>//#define DEBUG 1#define NUM 3int n=0;pthread_mutex_t mylock=PTHREAD_MUTEX_INITIALIZER;//互斥量pthread_cond_t qready=PTHREAD_COND_INITIALIZER;//条件变量void * thread_func(void *arg){int param=(int)arg;char c='A'+param;int ret,i=0;for (; i < 10; i++){pthread_mutex_lock(&mylock);while (param != n){#ifdef DEBUGprintf("thread %d waiting\n", param);#endifret = pthread_cond_wait(&qready, &mylock);if (ret == 0){#ifdef DEBUGprintf("thread %d wait success\n", param);#endif} else{#ifdef DEBUGprintf("thread %d wait failed:%s\n", param, strerror(ret));#endif}}// printf("%d ",param+1);printf("%c ",c);n=(n+1)%NUM;pthread_mutex_unlock(&mylock);pthread_cond_broadcast(&qready);}return (void *)0;}int main(int argc, char** argv) {int i=0,err;pthread_t tid[NUM];void *tret;for(;i<NUM;i++){err=pthread_create(&tid[i],NULL,thread_func,(void *)i);if(err!=0){printf("thread_create error:%s\n",strerror(err));exit(-1);}}for (i = 0; i < NUM; i++){err = pthread_join(tid[i], &tret);if (err != 0){printf("can not join with thread %d:%s\n", i,strerror(err));exit(-1);}}printf("\n");return 0;}

运行结果:

需要C/C++ Linux高级服务器架构师学习资料后台私信“资料”(包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等)

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

方法一,采用互斥量来实现生产者和消费者的同步。

流程图如下所示:

生产者:

对互斥量加锁判断缓冲区是否已满,如满,则跳到步骤4放入产品解锁互斥量,此时一个循环完成,进入下一循环。

消费者流程图类似与生产者流程图。

代码如下:

#include <stdio.h>  #include <stdlib.h>  #include <string.h>  #include <pthread.h>  #define NUMS 10  //表示生产,消费的次数  #define CAPACITY 5 //定义缓冲区最大值  int capacity = 0; //当前缓冲区的产品个数  pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;//互斥量    void *produce(void *args)  {      int i = 0;      for (; i < NUMS; )      {          pthread_mutex_lock(&mylock);//加锁          if (capacity >= CAPACITY) //当前产品个数大于等于缓冲区最大值,则不把产品放入缓冲区。          {              printf("缓冲区已满,无法放入产品\n");          } else {//将产品放入缓冲区              ++capacity;              printf("生产者存入一个产品, 缓冲区大小为:%d\n", capacity);              i++;          }          pthread_mutex_unlock(&mylock);      }      return ((void *) 0);  }    void * consume(void *args)  {      int i = 0;      for (; i < NUMS; )      {          pthread_mutex_lock(&mylock);          if (capacity > 0)           {              --capacity;              printf("消费者消耗一个产品,缓冲区大小为:%d\n", capacity);              i++;          } else      {             printf("缓冲区已空,无法消耗产品\n");          }          pthread_mutex_unlock(&mylock);      }      return ((void *) 0);  }    int main(int argc, char** argv) {        int err;      pthread_t produce_tid, consume_tid;      void *ret;      err = pthread_create(&produce_tid, NULL, produce, NULL);//创建线程      if (err != 0)       {          printf("线程创建失败:%s\n", strerror(err));          exit(-1);      }      err = pthread_create(&consume_tid, NULL, consume, NULL);      if (err != 0)      {          printf("线程创建失败:%s\n", strerror(err));          exit(-1);      }      err = pthread_join(produce_tid, &ret);//主线程等到子线程退出      if (err != 0)       {          printf("生产着线程分解失败:%s\n", strerror(err));          exit(-1);      }      err = pthread_join(consume_tid, &ret);      if (err != 0)      {          printf("消费者线程分解失败:%s\n", strerror(err));          exit(-1);      }      return (EXIT_SUCCESS);  } 

执行结果:

结果满足题意。但是这存在一个问题,极端情况下,生产者每次都加锁成功,那缓冲区会满,产品无法放入缓冲区。消费者会被饿死,因为他一直无法获得互斥量。方法二,解决了导致某一方饿死的可能性。

update:

在第一种方法中,当缓冲区满时,让生产者睡眠;当缓冲区空,让消费者睡眠。这样也能解决某一方老是加锁成功。

方法二:采用两个互斥量来完成

流程图如下:

运行截图:

上代码:

/*   * File:   main.c  * Author: root  *  * Created on 2012年5月22日, 上午9:35  */    #include <stdio.h>  #include <stdlib.h>  #include <string.h>  #include <pthread.h>    #define NUMS 10  //表示生产,消费的次数  #define CAPACITY 5 //定义缓冲区最大值      int capacity = 0; //当前缓冲区的产品个数  pthread_mutex_t mylockA=PTHREAD_MUTEX_INITIALIZER;  pthread_mutex_t mylockB=PTHREAD_MUTEX_INITIALIZER;    void *produce(void *args)  {      int i = 0,err;      for (; i < NUMS; )      {          pthread_mutex_lock(&mylockA);//加锁          if (capacity >= CAPACITY) //当前产品个数大于等于缓冲区最大值,则不把产品放入缓冲区。          {              printf("缓冲区已满,无法放入产品\n");          } else {//将产品放入缓冲区              ++capacity;              printf("生产者存入一个产品, 产品个数:%d\n", capacity);              i++;          }          err=pthread_mutex_unlock(&mylockB);      }      return ((void *) 0);  }    void * consume(void *args)  {      int i = 0;      for (; i < NUMS; )      {          pthread_mutex_lock(&mylockB);          if (capacity > 0)           {              --capacity;              printf("消费者消耗一个产品,产品个数:%d\n", capacity);              i++;          } else      {             printf("缓冲区已空,无法消耗产品\n");          }          pthread_mutex_unlock(&mylockA);      }      return ((void *) 0);  }    int main(int argc, char** argv) {        int err;      pthread_t produce_tid, consume_tid;      void *ret;      if(capacity==0)          pthread_mutex_lock(&mylockB);      else          if(capacity==CAPACITY)              pthread_mutex_lock(&mylockA);      err = pthread_create(&produce_tid, NULL, produce, NULL);//创建线程      if (err != 0)       {          printf("线程创建失败:%s\n", strerror(err));          exit(-1);      }      err = pthread_create(&consume_tid, NULL, consume, NULL);      if (err != 0)      {          printf("线程创建失败:%s\n", strerror(err));          exit(-1);      }      err = pthread_join(produce_tid, &ret);//主线程等到子线程退出      if (err != 0)       {          printf("生产着线程分解失败:%s\n", strerror(err));          exit(-1);      }      err = pthread_join(consume_tid, &ret);      if (err != 0)      {          printf("消费者线程分解失败:%s\n", strerror(err));          exit(-1);      }      return (EXIT_SUCCESS);  } 

六、有一个写者很多读者,多个读者可以同时读文件,但写者在写文件时不允许有读者在读文件,同样有读者读时写者也不能写。

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

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

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

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

#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( ;) –\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;  } 

七、编写程序完成如下功能:

1)有一int型全局变量g_Flag初始值为0

2) 在主线中起动线程1,打印“this is thread1”,并将g_Flag设置为1

3) 在主线中启动线程2,打印“this is thread2”,并将g_Flag设置为2

4) 线程序1需要在线程2退出后才能退出

5) 主线程在检测到g_Flag从1变为2,或者从2变为1的时候退出

atomic<int> flag(0);void worker1(future<int> fut) {//线程1      printf("this is thread1\n");    flag = 1;    fut.get();//线程1阻塞至线程2设置共享状态  get等待异步操作结束并返回结果    printf("thread1 exit\n");}void worker2(promise<int> prom) {//线程2      printf("this is thread2\n");//C++11的线程输出cout没有boost的好,还是会出现乱序,所以采用printf,有点不爽      flag = 2;    prom.set_value(10);//线程2设置了共享状态后,线程1才会被唤醒      printf("thread2 exit\n");}//利用promise future来控制线程退出的次序int main(){    promise<int> prom;    future<int> fut = prom.get_future();    thread one(worker1, move(fut));//注意future和promise不允许拷贝,但是具备move语义      thread two(worker2, move(prom));    while (flag.load() == 0);  ///将本线程从调用线程中分离出来,允许本线程独立执行    one.detach();    two.detach();    //exit(1);//主线程到这里退出      printf("main thread exit\n");    system("pause");    return 0;}

标签: #c语言多进程多线程面试题