多執行緒經典面試題

shuilaner_發表於2018-11-28

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

相關文章