开启辅助访问 切换到窄版

打印 上一主题 下一主题

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

[复制链接]
作者:未完续· 
版块:
嵌入式操作系统 linux 发布时间:2020-10-23 07:28:00
15430
楼主
跳转到指定楼层
| 只看该作者 回帖奖励 |倒序浏览 |阅读模式
一、线程与进程的区别?
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 #include #include #include #include //#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;void *tret;for(;i= 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  #include  #include  #include  #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  #include  #include  #include  #include  using namespace std; //读者与写者问题 #include  #include  #include  //设置控制台输出颜色 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; //先启动二个读者线程 for (i = 1; i

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表