goole面试题:有四个线程1、2、3、4。线程1的功能就是输出1,线程2的功能就是输出2,以此类推.........现在有四个文件ABCD

更多C++多线程面试题,见一份多线程面试题及参考答案

目录

创建线程

C++文件读写ofstream

互斥变量:临界区

事件

下一轮线程1,2,3,4要操作的文件编号

源代码:

参考文献:


有四个线程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....
请设计程序。

创建线程

创建线程优先使用_beginthreadex()

头文件:

#include <process.h>

函数定义:

unsigned long _beginthreadex( 
    void *security, 
    unsigned stack_size, 
	unsigned ( __stdcall *start_address )( void * ),
 	void *arglist, 
    unsigned initflag, 
    unsigned *thrdaddr 
);

security :线程的安全属性,NULL表示默认安全属性

stack_size:线程的堆栈大小,一般默认0

start_address:启动函数地址

arglist:参数列表,传递多个参数时用结构体,传结构体指针

initflag:新线程的初始状态,0表示立即执行,CREATE_SUSPEND(0x00000004)表示挂起

thrdaddr:用来接收线程ID 

for (i = 0; i < THREAD_NUM; i++)
		hdl[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, (void*)i, 0, NULL);

C++文件读写ofstream

#include <fstream>  
using namespace std;
ofstream         //文件写操作 内存写入存储设备   
ifstream         //文件读操作,存储设备读区到内存中  
fstream          //读写操作,对打开的文件可进行读写操作  

这里主要讲ofstream,ofstream是从内存到硬盘,其实所谓的流缓冲就是内存空间。

void open(const char* filename,int mode,int access); 
参数:  
filename:  要打开的文件名  
mode:    要打开文件的方式  
access:   打开文件的属性

mode:打开文件的方式在ios类(所以流式I/O的基类)中定义,有如下几种方式:

ios::in 为输入(读)而打开文件
ios::out 为输出(写)而打开文件
ios::ate 初始位置:文件尾
ios::app 所有输出附加在文件末尾
ios::trunc 如果文件已存在则先删除该文件
ios::binary 二进制方式

这些方式是能够进行组合使用的,以“或”运算(“|”)的方式打开文件的属性同样在ios类中也有定义:

0 普通文件,打开操作
1 只读文件
2 隐含文件
4 系统文件

对于文件的属性也可以使用“或”运算和“+”进行组合使用,这里就不做说明了。 

互斥变量:临界区

#include <windows.h>
CRITICAL_SECTION g_csFile;
//进入临界区
InitializeCriticalSection(&g_csFile);
EnterCriticalSection(&g_csfile);
LeaveCriticalSection(&g_csfile);
DeleteCriticalSection(&g_csFile);

事件

HANDLE CreateEvent(   
LPSECURITY_ATTRIBUTES lpEventAttributes, // 1.安全属性 
BOOL bManualReset,   // 2.复位方式,是否为手动
BOOL bInitialState,   // 3.初始状态,是否有信号 
LPCTSTR lpName     //4.对象名称   
);

 一般参数1和参数4为NULL

参数2,bManualReset,指定将事件对象创建成手动复原还是自动复原。
如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。
如果设置为FALSE,当事件被一个等待线程释放以后,系统将会自动将事件状态复原为无信号状态

参数3,bInitialState,指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。

CreateEvent(NULL, FALSE, FALSE, NULL)

 创建的事件,初始状态为无信号状态,事件被释放后,自动复原为无信号状态。

CEvent::SetEvent()把对象设置为有信号状态

WaitForSingleObject(g_hThreadEvent[num], INFINITE);

DWORD WaitForSingleObject( HANDLE hHandle, DWORDdwMilliseconds);

有两个参数,分别是THandle和Timeout(毫秒单位)。

如果想要等待一条线程,那么你需要指定线程的Handle,以及相应的Timeout时间。当然,如果你想无限等待下去,Timeout参数可以指定系统常量INFINITE。

此处等待g_hThreadEvent[num]有信号,即等待SetEvent(ThreadEvent[num])激活事件信号。 

下一轮线程1,2,3,4要操作的文件编号

NEXT_LOOP[FILE_NUM] = { 0,1,2,3, }

代表线程1,2,3,4分别要输出的文件编号,

第一轮线程1输出文件A,线程2输出文件B,线程3输出文件C ,线程输出文件D

第二轮,线程1输出文件D,线程2输出文件A,线程3输出文件B,线程4输出文件C,则

NEXT_LOOP[FILE_NUM] = { 3,0,1,2, }

第三轮: 

NEXT_LOOP[FILE_NUM] = { 2,3,0,1, }

即,下一轮线程要操作的文件编号为:

	NEXT_LOOP[(num + 1) % (FILE_NUM)] = FILE_THREAD[num];

源代码:

// HJ49_m多线程.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <windows.h>
#include <process.h>
#include <fstream>
using namespace std;

CRITICAL_SECTION g_csfile;
//线程个数
const int THREAD_NUM = 4;
unsigned int __stdcall Fun(void *pPM);

//文件个数
const int FILE_NUM = 4;
ofstream file[FILE_NUM];

//互斥事件
HANDLE g_hThreadEvent[THREAD_NUM];
HANDLE g_OK;
//每个线程输出的字符个数
const int Loop = 6;

//某个线程要写入的文件
int FILE_THREAD[FILE_NUM] = { 0,1,2,3 };
int NEXT_LOOP[FILE_NUM] = { 0,1,2,3 };

int main()
{
	InitializeCriticalSection(&g_csfile);
	//1.创建线程
	HANDLE hdl[THREAD_NUM];
	for (int i = 0; i < THREAD_NUM;i++)
	{
		hdl[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, (void*)i, 0, NULL);
	}

	//3.创建线程互斥事件并激活线程1
	for (int i = 0; i < THREAD_NUM; i++)
	{
		g_hThreadEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
	}
	g_OK = CreateEvent(NULL, FALSE, FALSE, NULL);
	//2.打开文件
	char filename[] = "A.txt";
	for (int i = 0; i < FILE_NUM;i++)
	{
		filename[0] = i + 'A';
		file[i].open(filename, ios::trunc);
		if (file[i].fail())
		{
			printf("打开文件%s失败", filename);
			continue;
		}
	}



	SetEvent(g_hThreadEvent[0]);

	//4.等待线程完成6次打印
	//WaitForMultipleObjects(THREAD_NUM, g_hThreadEvent, TRUE, INFINITE);
	WaitForSingleObject(g_OK,INFINITE);
	printf("最后结果:");

	//5.销毁线程
	for (int i = 0; i < THREAD_NUM;i++)
	{
		CloseHandle(hdl[i]);
		CloseHandle(g_hThreadEvent[i]);
	}

	//6.关闭文件流:
	for (int i = 0; i < FILE_NUM;i++)
	{
		file[i].close();
	}
	DeleteCriticalSection(&g_csfile);

    return 0;
}

//7.实现线程函数
unsigned int __stdcall Fun(void *pPM)
{
	//当前线程id
	int num = (int)pPM;

	for (int i = 0; i < Loop;i++)
	{
		WaitForSingleObject(g_hThreadEvent[num], INFINITE);
		EnterCriticalSection(&g_csfile);
		printf("  线程 %d 正在向%c文件写入,下一次对%c文件进行操作的是线程%d\n",num+1,FILE_THREAD[num]+'A', FILE_THREAD[num] + 'A',(num+1)%(THREAD_NUM)+1);
		file[FILE_THREAD[num]] << num + 1 << " ";

		Sleep(200);

		NEXT_LOOP[(num + 1) % FILE_NUM] = FILE_THREAD[num];
		if(num + 1 == FILE_NUM)
		{
			printf("\n");
			memcpy(FILE_THREAD, NEXT_LOOP, sizeof(int)*FILE_NUM);
		}
		LeaveCriticalSection(&g_csfile);
		SetEvent(g_hThreadEvent[(num + 1) % THREAD_NUM]);
	}

	if (num + 1 == THREAD_NUM)
	{
		SetEvent(g_OK);
	}
	return 0;
}

参考文献:

Google面试题

C++文件读写详解(ofstream,ifstream,fstream)

ofstream的使用方法--超级精细。C++文件写入、读出函数(转)

猜你喜欢

转载自blog.csdn.net/haimianjie2012/article/details/111247194