C++多线程实例之互斥量同步

要点:将互斥量 + 条件变量/事件对象封装为了一个类CMutexLock,该类同时支持windows和linux下的互斥量同步。对熟悉windows和linux下面的多线程开发很有帮助。下面的代码可以直接在VS2008,2010中编译通过,linux下也是没问题的。如果你觉得写得不错也可以直接移植到你的代码库中,比较适合多线程中采用生产者-消费者这样的场景,比如子线程读取和解析文件生产给其它线程用,网络接收子线程接收处理数据给其它线程使用等。

强烈建议有需要的跑起来调试下(本项目类型是VS win32项目cpp中需要#include "stdafx.h",其它类型项目或者linux下简单修改下即可),代码注释中有详细的说明。如果你觉得有更好的做法,或者那里注释错了,非常欢迎你的回复。本代码也是参考了很多网上的资料,感谢参考文章中的各位作者。

工程设置:

1.pthread多线程库使用:

pthread库源代码:ftp://sources.redhat.com/pub/pthreads-win32/
本项目下载的是:pthreads-w32-2-9-1-release.zip 解压Pre-built.2就是要包含的库,pthreadVC2.dll拷贝到C:\Windows\System32下。

VC工程设置:
头文件:../../pthreads-w32-2-9-1-release\Pre-built.2\include
库目录:../../pthreads-w32-2-9-1-release\Pre-built.2\lib\x86
库:pthreadVC2.lib
代码包含头文件:#include <pthread.h>

2.windows下
VC工程设置ProjectàSetting-->C/C++-->Code generation->run-time library 选择Multi-threaded 或者Multithreaded。
即使用: MT或MD。
代码包含头文件:
#include <Windows.h>
#include <process.h>

多线程代码:

例子,具体用法和分析见代码注释:

MutexLock.h

#ifndef _MUTEXLOCK_H_
#define _MUTEXLOCK_H_

#ifdef WIN32
//#define WINOS // 这里可以注释掉就是linux形式的互斥量
#else
#undef  WINOS
#endif

#ifdef WINOS
#include <Windows.h>
#include <process.h> 
#else
#include <pthread.h>
//来自ftp://sources.redhat.com/pub/pthreads-win32/
/* 获取互斥量属性对象在进程间共享与否的标志 
int pthread_mutexattr_getpshared (__const pthread_mutexattr_t *__restrict __attr, \  
								  int *__restrict __pshared);  
 设置互斥量属性对象,标识在进程间共享与否
int pthread_mutexattr_setpshared (pthread_mutexattr_t *__attr, int __pshared); 
底层库都像一个状态机,包括socket,pthread,directx,opengl,openal...
这是对外统一接口,松耦合,但是又可能需要调整内部的参数,那么必须采用这样的方式,不论编写的语言是基于过程还是基于对象的。
 */
/*参考文章:http://www.ibm.com/developerworks/cn/linux/l-cn-mthreadps/
http://blog.csdn.net/anonymalias/article/details/9093733
https://software.intel.com/zh-cn/blogs/2011/03/24/linux-windows
*/
#endif

class CMutexLock
{
public:
	CMutexLock();
	~CMutexLock();
	void init();
	void release();
	void lock();
	void unlock();// 设计的时候,不要unwaite放置到unlock里面去,否则会导致职责不分明,如果有内部控制的还会导致无法唤醒。
	void waite();// 当获取不到数据,那么waite挂起线程,等待其它线程通知释放
	void unwaite();// 生产了数据那么需要调用unwaite.
private:
	#ifdef WINOS
		HANDLE m_mutex;
		HANDLE m_event;//事件如果有信号那么可以正常执行,如果无信号那么只能等待
	#else
		pthread_mutex_t m_mutex;
		pthread_cond_t m_condition;
		pthread_mutexattr_t m_mutexAttr;
	#endif
};

#endif

MutexLock.cpp

#include "stdafx.h"
#include "MutexLock.h"
/*参考文章:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms682411%28v=vs.85%29.aspx
http://blog.csdn.net/anonymalias/article/details/9080881
http://blog.csdn.net/anonymalias/article/details/9174403
*/
CMutexLock::CMutexLock()
{
	
#ifndef WINOS
	m_mutex = NULL;
	m_condition = NULL;
#else
	m_mutex = NULL;
	m_event = NULL;
#endif
}
CMutexLock::~CMutexLock()
{
	release();
}
void CMutexLock::release()
{
#ifdef WINOS
	CloseHandle(m_mutex);//所有内核对象,或者用其它方式创建的,都可以用closeHandle将引用计数减1。
	m_mutex = NULL;
#else
	pthread_mutexattr_destroy(&m_mutexAttr);
	pthread_mutex_destroy(&m_mutex);
	pthread_cond_destroy(&m_condition);
	m_mutex = NULL;
	m_condition = NULL;
#endif
}

void CMutexLock::init()
{
#ifdef WINOS
	// arg1 是NULL,互斥量用默认的安全描述信息,这个时候子进程不能继承该互斥量.
	// arg2 是当前指明互斥量指向的线程为空,且被引用的次数是0,没有线程/进程拥有该互斥量;否则当前线程拥有该互斥量。
	// arg3 互斥量的名字
	m_mutex = CreateMutex(NULL, FALSE, NULL);
	DWORD dwLastError = GetLastError();
	if( dwLastError == ERROR_ALREADY_EXISTS)
	{
		CloseHandle(m_mutex);
		m_mutex = NULL;
	}
	//事件是有信号线程不阻塞,无信号阻塞线程睡眠。
	// arg1是事件属性。
	// arg2是手动还是自动调用ResetEvent将事件设置为无信号,SetEvent是将事件设置为有信号
	// ResetEvent是否手动设置为无信号,WaitForSingleObject后如果是自动方式那么会自动调用ResetEvent将事件设置为无信号。
	// arg3是初始状态信号,一般设置为FALSE无信号,让线程挂起阻塞。
	// arg4是线程的名字。
	m_event = CreateEvent(NULL, FALSE, FALSE, NULL);
#else
	// arg1是初始化的互斥量,arg2是pthread_mutexattr_t属性指针,如果是NULL,那么没有线程拥有该初始化好的互斥量。
	int nResult = pthread_mutex_init(&m_mutex, NULL);
	if(nResult == 0)
	 {
		 printf("pthread_mutex_init result OK.\n");
	 }
	 else
	 {
		 printf("pthread_mutex_init result error:%d\n", nResult);
	 }
	pthread_mutexattr_init(&m_mutexAttr); 
	// 设置 recursive 属性,使得linux下可以递归加锁,避免递归加锁死锁。
	pthread_mutexattr_settype(&m_mutexAttr,PTHREAD_MUTEX_RECURSIVE_NP); 
	pthread_mutex_init(&m_mutex, &m_mutexAttr);

	pthread_cond_init(&m_condition, NULL);
#endif
}

void CMutexLock::lock()
{
#ifdef WINOS
	// arg2是等待毫秒时间,INFINITE是永远等待,直到该内核对象被触发可用;该函数是一个异步调用函数,互斥量拥有线程id非0,
	// 那么该函数将被挂起阻塞,释放当前CPU拥有权,当被其它线程释放互斥量拥有线程id为0,将会唤醒当前阻塞的线程重新获取互斥量。
	WaitForSingleObject(m_mutex, INFINITE);
	/*if(WaiteforSingleObject(m_hMutex, dwMilliSec) == WAIT_OBJECT_0)
	{
		return true;
	}
	return false;
	*/
#else
	// 锁定互斥锁,如果该互斥锁被其它线程拥有,那么将被挂起阻塞,指定可用才回调返回;
	// 线程自己多次锁定将会导致死锁;两个线程需要多个互斥锁相互等待对方的互斥锁,也会导致死锁。
	pthread_mutex_lock(&m_mutex); 
#endif
}

void  CMutexLock::waite()
{
#ifdef WINOS
	WaitForSingleObject(m_event, INFINITE);// 等待的事件,和时间
#else
	//会自动调用pthread_mutex_unlock(&m_mutex)释放互斥量,将当前线程挂起阻塞,等待对方线程pthread_cond_signal通知唤醒,
	// 唤醒后pthread_cond_wait会调用pthread_mutex_lock重新锁定互斥量。
	// pthread_cond_timedwait是阻塞一段时间。
	pthread_cond_wait(&m_condition, &m_mutex);
#endif
}

void CMutexLock::unwaite()
{
#ifdef WINOS
	SetEvent(m_event);//设置为有信号,唤醒等待事件挂起的线程。
#else
	pthread_cond_signal(&m_condition);//pthread_cond_broadcast(pthread_cond_t * cond)唤醒在条件上等待的所有线程。
#endif

}

void CMutexLock::unlock()
{
#ifdef WINOS
	ReleaseMutex(m_mutex);// 将互斥量释放,会通知到WaitForSingleObject.
#else
	pthread_mutex_unlock(&m_mutex);
#endif
}
main.cpp
/*多线程的一些总结:
一、互斥量是拥有线程ID的,如果互斥量没有线程ID那么当前线程可以获得互斥量,互斥量函数非阻塞;否则互斥量函数将阻塞当前线程。
    linux下条件变量初始化时是没有绑定互斥量的(无信号的),只要waite都会释放当前互斥量,阻塞当前线程,直到有signal发送过来才会唤醒。
	window下的事件对象,事件对象无信号情况下会阻塞当前线程,通过SetEvent(m_event)可以触发事件(signal),让当前阻塞的线程唤醒。
二、采用等待机制等有效的提高程序的CPU利用率,注意等待时需要先释放所用拥有的锁(尽管之前释放过,),否则会导致死锁。
s_mutexFileContent.unlock();// 不加这句linux的pthread库会导致死锁,linux不能递归加锁否则会导致死锁,windows下却可以。
s_mutexRequent.unlock(); // 不加这句,windows下的WaitForSingleObject不会先释放互斥量锁,也会导致死锁。

//s_mutexRequent.waite();
三、pthread.h库下默认创建的线程是可结合的,每个可结合线程都应该要么被显示地回收,即调用pthread_join;
要么通过调用pthread_detach函数分离子线程,子线程被分离后不能再结合了。
pthread_detach(s_loadingThread);

四、window下的WaitForSingleObject线程未运行时候是未触发的,当线程运行完那么是触发的,所以可以等到到线程。
//返回WAIT_OBJECT_0在指定时间内等到到,WAIT_TIMEOUT超时,WAIT_FAILED有错误。
HANDLE handle = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);  
WaitForSingleObject(handle, INFINITE);
五、windows下众多多线程函数的选择;_beginthread是_beginthreadex的子集参数受限制,释放会有差异,所以用_beginthreadex即可。
_beginthreadex内部会调用CreateThread(),会给C运行库函数开辟堆资源,所以要用_endthreadex和CloseHandle来避免内存泄露。
CreateThread()没有开辟堆资源,所以在C运行库中可能导致多线程数据异常风险,但是在Win32/MFC C++运行库中可以放心使用。
AfxBeginThread()是MFC中的多线程,分工作线程无消息循环,界面线程有消息循环,可以让当前线程创建,挂起,唤醒,终止。
windows下线程常用函数:DWORD SuspendThread(HANDLE hThread);DWORD ResumeThread(HANDLE hThread);BOOL SetThreadPriority(HANDLE hThread,int nPriority);
VOID ExitThread(DWORD dwExitCode); BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode); 
一般线程的挂起/唤醒都通过同步对象来实现。

如果在代码中有使用标准C运行库中的函数时,尽量使用_beginthreadex()来代替CreateThread()。
window下的多线程底层都是对CreateThread的封装。
如果在除主线程之外的任何线程中进行一下操作,你就应该使用多线程版本的C runtime library,并使用_beginthreadex和_endthreadex,CloseHandle:
1 使用malloc()和free(),或是new和delete
2 使用stdio.h或io.h里面声明的任何函数
3 使用浮点变量或浮点运算函数
4 调用任何一个使用了静态缓冲区的runtime函数,比如:asctime(),strtok()或rand()

六、linux和window下互斥量和条件变量的区别
1.linux连续上锁会死锁,可以用 pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP); 解决。windows连续上锁不会死锁。
2.SetEvent(m_event)后等待事件对象一直是有signal的,后面waite的不会阻塞;linux下pthread_cond_signal不会一直有信号,后面waite的将会阻塞。
3.pthread_cond_wait()后不需要重新加锁,WaitForSingleObject/SignalObjectAndWait后需要重新加锁。
4.linux的pthread_cond_timedwait等待的是绝对时间1970-01-01 00:00:00 开始的时间,window的WaitForSingleObject是一个从当前开始的相对时间。 
5.linux的线程释放,非游离的线程需要主线程调用pthread_join等待子线程结束并释放子线程的栈寄存器,游离的线程需要设置为属性为游离,
或者创建后用pthread_detach设置,子线程结束时候系统会回收子线程的资源。这样才能避免内存泄露。
windows下的释放:_beginthreadex创建的要调用_endthreadex和CloseHandle,其它方式创建的线程ExitThread或CloseHanle即可。
*/
#include "stdafx.h"
#include "MutexLock.h"
#include <iostream>
#include <deque>
#include <string>
using namespace std;
#include <windows.h>

// 异步线程
#ifdef WINOS
static HANDLE s_loadingThread = NULL;
#else
static pthread_t s_loadingThread;
#endif
// 异步读取文件的互斥量
static CMutexLock s_mutexRequent;
// 异步渲染的互斥量
static CMutexLock s_mutexFileContent;

// 根对象,派生Node
class Object
{
public:
	Object()
	{
		m_dObjID = 0;
	}
protected:
	double m_dObjID;
};
// 异步加载后的回调函数
typedef  void (* ObjectCallBack)(Object* );

typedef struct tagAsyncFileData
{
	bool m_bLoadOK;
	Object *m_pTarget;
	ObjectCallBack m_pCallback;
	string strFilePath;
	tagAsyncFileData()
	{
		m_bLoadOK = false;
		m_pTarget = NULL;
		m_pCallback = NULL;
		strFilePath.clear();
	}
}AsyncFileData;

static deque<AsyncFileData*> s_dataFileRequent;

typedef struct tagFileBufferData
{
	AsyncFileData *m_pAsyncFileData;
	char *m_pBuffer;
	tagFileBufferData()
	{
		m_pAsyncFileData = NULL;
		m_pBuffer = NULL;
	}
}AsyncFileBufferData;

static deque<AsyncFileBufferData*> s_dataFileContent;

#ifdef WINOS
unsigned __stdcall AsyncLoad(void *pParameter)  
#else
static void* AsyncLoad(void *pParameter)
#endif
{
	while(1)
	{
		AsyncFileData *pFileData = NULL;
		s_mutexRequent.lock();
		if(s_dataFileRequent.empty())
		{
			// 如果没有数据过来那么释放当前的锁,挂起CPU等待
			printf("子线程,因没有请求的文件而等待!\n");
			s_mutexFileContent.unlock();// 不加这句linux的pthread库会导致死锁。
			s_mutexRequent.unlock(); // 不加这句,windows下的WaitForSingleObject不会先释放互斥量锁,也会导致死锁。
			s_mutexRequent.waite();
			
			continue;
		}
		else
		{
			pFileData = s_dataFileRequent.front();
			s_dataFileRequent.pop_front();
		}
		s_mutexRequent.unlock();
		

		// 得到数据处理
		if(pFileData != NULL)
		{
			// 异步加载数据,此次mmap还是fread方式略去,直接设置加载OK
			//fopen(pFileData->strFilePath.c_str(), "rb");
			Sleep(1000);
			pFileData->m_bLoadOK = true;
			//pFileData.m_pTarget
			AsyncFileBufferData *pAsyncBuffer  = new AsyncFileBufferData;
			pAsyncBuffer->m_pAsyncFileData = pFileData;
			char *pContent = "data from pFileData's strFilePath...";
			int nContenLen = strlen(pContent) + 1;
			pAsyncBuffer->m_pBuffer = new char[nContenLen];
			strcpy_s(pAsyncBuffer->m_pBuffer, nContenLen, pContent);
			printf("子线程 读取文件: %s\n", pAsyncBuffer->m_pAsyncFileData->strFilePath.c_str());
			
			// 异步处理锁
			s_mutexFileContent.lock();
			// 解析好的数据放置进来
			s_dataFileContent.push_back(pAsyncBuffer);
			s_mutexFileContent.unlock();
			s_mutexFileContent.unwaite();
		}
	}
#ifdef WINOS
	_endthreadex( 0 );// 释放_beginthreadex分配的堆资源,且还要用CloseHandle释放
	return 0;
#endif
}

int main(int argc, char* argv[])  
{ 
	s_mutexRequent.init();
	s_mutexFileContent.init();
#ifdef WINOS
	unsigned int uiThreadID;
	s_loadingThread = (HANDLE)_beginthreadex(NULL, 0, AsyncLoad, NULL, CREATE_SUSPENDED, &uiThreadID); 
	/*_CRTIMP uintptr_t __cdecl _beginthreadex(_In_opt_ void * _Security, _In_ unsigned _StackSize,
		_In_ unsigned (__stdcall * _StartAddress) (void *), _In_opt_ void * _ArgList, 
		_In_ unsigned _InitFlag, _In_opt_ unsigned * _ThrdAddr);*/
	if(s_loadingThread == NULL)
	{
		printf("pthread_create error!");
		return 0;
	}
	ResumeThread(s_loadingThread); 
#else
	if( pthread_create(&s_loadingThread, NULL, AsyncLoad, NULL) != 0)
	{
		printf("pthread_create error!");
		return 0;
	}
	pthread_detach(s_loadingThread);
#endif
	// 在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached)。一个可结合的线程能够被其他线程收回其资源和杀死。
	// 在被其他线程回收之前,它的存储器资源(例如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,
	// 它的存储器资源在它终止时由系统自动释放。默认情况下,线程被创建成可结合的。为了避免存储器泄漏,
	// 每个可结合线程都应该要么被显示地回收,即调用pthread_join;要么通过调用pthread_detach函数被分离。

	// pthread_detach(s_loadingThread);分离,运行结束后子线程会自动释放自己资源,不需要pthread_join也可以完全释放资源。
	//   void* ret = NULL;
	// pthread_join(_subThreadInstance, &ret);主线程一直等待直到等待的线程结束自己才结束,主线程可以清理其它线程的栈寄存器。
	// pthread_self()获取自身线程的id.
	// 线程的被动结束分为两种,一种是异步终结,另外一种是同步终结。异步终结就是当其他线程调用 pthread_cancel的时候,
	// 线程就立刻被结束。而同步终结则不会立刻终结,它会继续运行,直到到达下一个结束点(cancellation point)。
	// 当一个线程被按照默认的创建方式创建,那么它的属性是同步终结。
	static int fileCount = 0;
	while(1)
	{
		s_mutexRequent.lock();
		AsyncFileData* m_pFileData = new AsyncFileData();
		m_pFileData->m_bLoadOK = false;
		m_pFileData->m_pCallback = NULL;
		m_pFileData->m_pTarget = NULL;
		fileCount++;
		char szFileBuffer[256];
		sprintf_s(szFileBuffer,"文件名 %d.", fileCount);
		m_pFileData->strFilePath = szFileBuffer;
		printf("主线程,请求读取文件: %s\n", m_pFileData->strFilePath.c_str());
		s_dataFileRequent.push_back(m_pFileData);
		s_mutexRequent.unlock();
		s_mutexRequent.unwaite();

		// 其它逻辑
		Sleep(1000);

		while(1)
		{
			AsyncFileBufferData *pAsyncBuffer = NULL;

			s_mutexFileContent.lock();
			if(s_dataFileContent.empty())
			{
				printf("主线程,因没有解析好的数据等待!\n");
				s_mutexRequent.unlock();// 请求锁需要释放,否则会导致问题
				s_mutexFileContent.unlock();
				s_mutexFileContent.waite();// 读取线程还没解析好等待
				continue;
			}

			pAsyncBuffer = s_dataFileContent.front();
			s_dataFileContent.pop_front();
			s_mutexFileContent.unlock();

			if(pAsyncBuffer != NULL)
			{
				printf("主线程,得到读取线程解析后的文件:%s, 数据: %s\n", pAsyncBuffer->m_pAsyncFileData->strFilePath.c_str(), pAsyncBuffer->m_pBuffer);
				delete pAsyncBuffer->m_pAsyncFileData;
				delete [] pAsyncBuffer->m_pBuffer;
				delete pAsyncBuffer;
				pAsyncBuffer = NULL;

				// 其它逻辑
				Sleep(1000);
				break;
			}	
		}// end while 2
	
	} //  end while 1

	s_mutexRequent.release();
	s_mutexFileContent.release();
#ifdef WINOS
	CloseHandle(s_loadingThread);
#else
	// 设置了pthread_detach(s_loadingThread),退出时会自动释放,
	// 否则需要pthread_join()等待可结合的线程终止被释放它的栈寄存器资源.
#endif
	return 0;  
}  


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MFC C++是一种用于Windows编程的框架,其中包含了丰富的类库和功能,用于开发用户界面和处理系统操作。而互斥则是一种同步机制,用于确保在多线程环境中对共享资源的完整性和一致性。读者问题是经典的并发控制问题,通常描述为多个读者和写者同时访问一个共享资源(如文件、数据库等)的场景。 在MFC C++中,我们可以利用互斥来实现读者问题的解决方案。具体步骤如下: 1. 创建互斥对象:使用函数CreateMutex()来创建一个互斥对象,用于对共享资源进行加锁和解锁操作。 2. 定义读者线程函数:通过创建多个读者线程的方式来模拟多个读者同时访问共享资源的场景。在读者线程函数中,需要使用函数WaitForSingleObject()来等待互斥对象的可用性,即等待获取锁。 3. 定义写者线程函数:通过创建一个写者线程,模拟写者访问共享资源的场景。在写者线程函数中,同样需要使用函数WaitForSingleObject()来等待互斥对象的可用性。 4. 读者访问共享资源:在读者线程函数中,当成功获取到互斥对象的锁后,可以执行读取共享资源的操作。 5. 写者访问共享资源:在写者线程函数中,当获取到互斥对象的锁后,可以执行写入共享资源的操作。 6. 释放互斥对象:在读者线程和写者线程函数执行完相应的操作后,需要使用函数ReleaseMutex()来释放互斥对象的锁。 通过上述步骤,我们可以实现一个基本的MFC C++多线程读者问题例子。在实际应用中,可能还需要考虑一些细节,如读者优先、写者优先的调度算法、条件变等。但以上的解决方案是基本的框架,可以帮助我们理解和实现多线程读者问题。 ### 回答2: 多线程读者问题是一个经典的并发控制问题,它包含多个读者和一个写者线程,读者可以同时读取共享资源,而写者独占资源进行写操作。在MFC C++编程中,可以利用互斥来实现对共享资源的访问控制,下面是一个使用互斥实现多线程读者问题的例子。 假设有一个全局变sharedData用于表示共享资源,初始值为0。 首先,需要定义两个互斥readMutex和writeMutex,一个信号readCount用于表示当前读者数。 然后,创建读者线程函数ReaderThread和写者线程函数WriterThread。 在读者线程函数中,首先需要对readMutex进行加锁操作,以保证在读者数发生变化时的互斥操作。接着将readCount加一,如果读者数为1,则需要对writeMutex进行加锁操作,以防止写者线程读取共享资源。最后,对readMutex进行解锁操作。 接下来,读者通过获取共享资源的值,进行读取操作,可以将共享资源的值输出到控制台上。最后,以相同的方式对readMutex进行加锁和解锁操作,再将readCount减一,如果读者数为0,则对writeMutex进行解锁操作。 在写者线程函数中,首先需要对writeMutex进行加锁操作,以保证写者线程独占共享资源。接着对共享资源进行修改操作,可以将共享资源的值加1,然后输出修改后的值到控制台上。最后,对writeMutex进行解锁操作。 在主函数中,创建两个读者线程和一个写者线程,并等待线程的执行完成。 通过以上的互斥操作,可以保证在多线程环境下,读者可以同时读取共享资源,而写者线程独占资源进行写操作。这样可以确保了并发环境下对共享资源的正确访问。 ### 回答3: 多线程读者问题是指有多个线程同时读取共享资源,并且在有写入者操作时禁止读取。在MFC C++中,可以利用互斥Mutex来实现多线程读者问题。 首先,定义一个共享资源(例如一个全局变),用于表示要访问的数据。 ``` int sharedData = 0; ``` 然后,创建两个互斥Mutex,一个用于同步读取操作,一个用于同步写入操作。 ``` HANDLE readMutex = CreateMutex(NULL, FALSE, NULL); HANDLE writeMutex = CreateMutex(NULL, FALSE, NULL); ``` 接下来,创建读者线程和写者线程,每个线程都会对互斥进行操作。读者线程首先尝试获取读取互斥,如果成功获取到互斥,就可以读取共享资源,然后释放互斥。写者线程首先尝试获取写入互斥,如果成功获取到互斥,就可以写入共享资源,然后释放互斥。 下面是读者线程的例子代码: ``` DWORD WINAPI ReaderThread(LPVOID lpParam) { while (true) { WaitForSingleObject(readMutex, INFINITE); // 请求读取互斥 // 读取共享资源的代码 // ... ReleaseMutex(readMutex); // 释放读取互斥 Sleep(1000); // 休眠一段时间,模拟读取操作 } return 0; } ``` 下面是写者线程的例子代码: ``` DWORD WINAPI WriterThread(LPVOID lpParam) { while (true) { WaitForSingleObject(writeMutex, INFINITE); // 请求写入互斥 // 写入共享资源的代码 // ... ReleaseMutex(writeMutex); // 释放写入互斥 Sleep(2000); // 休眠一段时间,模拟写入操作 } return 0; } ``` 最后,创建读者线程和写者线程,启动它们即可实现多线程读者问题。 ``` HANDLE hReaderThread = CreateThread(NULL, 0, ReaderThread, NULL, 0, NULL); HANDLE hWriterThread = CreateThread(NULL, 0, WriterThread, NULL, 0, NULL); WaitForSingleObject(hReaderThread, INFINITE); WaitForSingleObject(hWriterThread, INFINITE); ``` 以上是利用互斥实现多线程读者问题的一个例子,通过使用互斥来保护共享资源的读取和写入操作,确保线程安全。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值