windows编程之多线程总结

用于线程互斥的方法有:原子锁,关键区域(CriticalSection),互斥量(Mutex),

用于线程同步的方法有:事件(Event),信号量(Semaphore),定时器(这里我们不谈)。

原子锁:

我们都知道简单的i++,并不是原子操作,在内部其实分了几步来做,在这种情况下使用多线程就会出问题。所以我们可以采用原子锁,但是这种方法有一定的局限性,那就是只能是执行加减的简单数据的操作,如果涉及到复杂的数据结构,那么就会出问题。

InterlockedIncrement(LONG volatile*Addend);

InterlockedDecrement(LONG volatile*Addend);

InterlockedExchangeAdd(LONG volatile*Addend, LONGValue);

InterlockedExchange(LONG volatile*Target, LONGValue);

例子:

没有使用原子锁的情况:

#include <stdio.h>
#include <windows.h>
#include <process.h>

volatile long g_nLoginCount; 
unsigned int __stdcall Fun(void *pPM); 
const DWORD THREAD_NUM = 50;
unsigned int __stdcall ThreadFun(void *pPM)
{
	g_nLoginCount++;
	return 0;
}
int main()
{
	int num= 20;
	while (num--)
	{	
		g_nLoginCount = 0;
		int i;
		HANDLE  handle[THREAD_NUM];
		for (i = 0; i < THREAD_NUM; i++)
			handle[i] = (HANDLE )_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);
		WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
		for(i=0;i<THREAD_NUM;i++)
		{
			CloseHandle(handle[i]);

		}
		printf("%d个线程执行i++,结果是%d\n", THREAD_NUM, g_nLoginCount);
		
	}
	return 0;
}
这里解释下为什么搞几个线程,还弄的这么复杂,一个while循环,一个for循环,这是因为不能一次创建很多线程,否则会出问题,你自己可以试试依次性创建100个线程试试,会出问题的。貌似一次最多只能创建64个线程。所以这里采用了这么复杂的方法。
windows编程之多线程总结_第1张图片

使用原子锁的情况:

unsigned int __stdcall ThreadFun(void *pPM)
{
	g_nLoginCount++;
	InterlockedIncrement(&g_nLoginCount);
	return 0;
}
windows编程之多线程总结_第2张图片

关键区域(CriticalSection)

一般线程互斥用的多的是这种情况。

void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection); -----------初始化关键区域

void DeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);    -----------删除关键区域

void EnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);     -----------进入关键区域
void LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);    -----------退出关键区域

一般错误的情况:
#include <stdio.h>
#include <process.h>
#include <windows.h>
long g_nNum;
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = 10;
int main()
{

	HANDLE  handle[THREAD_NUM];	
	g_nNum = 0;
	int i = 0;
	while (i < THREAD_NUM) 
	{
		handle[i++] = (HANDLE)_beginthreadex(NULL, 0, Fun, NULL, 0, NULL);
		
	}
	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);

	for(i=0;i<sizeof(handle);i++)
	{
		CloseHandle(handle[i]);
	}

	return 0;
}
unsigned int __stdcall Fun(void *pPM)
{
	Sleep(50);
	g_nNum++;
	Sleep(0);
	printf("当前计数为:%d\n",g_nNum);
	return 0;
}

windows编程之多线程总结_第3张图片
当使用了关键区域的情况:
CRITICAL_SECTION g_csThreadCode;
InitializeCriticalSection(&g_csThreadCode);

DeleteCriticalSection(&g_csThreadCode);


unsigned int __stdcall Fun(void *pPM)
{
	
	Sleep(50);
	EnterCriticalSection(&g_csThreadCode);
	g_nNum++;
	Sleep(0);
	printf("当前计数为:%d\n",g_nNum);
	LeaveCriticalSection(&g_csThreadCode);
	return 0;
}

windows编程之多线程总结_第4张图片

事件(Event)

HANDLECreateEvent(

 LPSECURITY_ATTRIBUTESlpEventAttributes,

 BOOLbManualReset,

 BOOLbInitialState,

 LPCTSTRlpName

);---------创建事件。

HANDLEOpenEvent(

 DWORDdwDesiredAccess,

 BOOLbInheritHandle,

 LPCTSTRlpName     

);------------打开事件

BOOLSetEvent(HANDLEhEvent);----------触发事件

BOOLResetEvent(HANDLEhEvent);-----------将事件设为未触发


一般事件分成两种: 手动置位事件(TRUE)自动置位事件(FALSE),这是在CreateEvent函数的第二个参数指定的。它们的区别是:手动置位事件当调用SetEvent后,所有等待这个事件的线程激活,而自动置位事件调用SetEvent后,只有一个线程被激活。自动置位事件不用调用ResetEvent函数,因为系统会自动帮你置为未触发,而手动置位事件还要调用ResetEvent函数。


在展示事件之前,我们先来说说线程互斥和线程同步之间的区别吧。我的理解是:线程互斥是相当于两个写者同时去写一个文件,造成的混乱;线程同步相当于当有读者在读文件的时候,写者还在不停的写造成的混乱。
在以前的基础上,我们把线程ID传进去。(主线程去写,子线程去读)。

没有用事件的情况:
#include <stdio.h>
#include <process.h>
#include <windows.h>
long g_nNum;
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = 10;

CRITICAL_SECTION g_csThreadCode;
int main()
{

	InitializeCriticalSection(&g_csThreadCode);

	HANDLE  handle[THREAD_NUM];	
	g_nNum = 0;
	int i = 0;
	while (i < THREAD_NUM) 
	{
		handle[i++] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
	
	}
	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);

	for(i=0;i<sizeof(handle);i++)
	{
		CloseHandle(handle[i]);
	}
	DeleteCriticalSection(&g_csThreadCode);
	return 0;
}
unsigned int __stdcall Fun(void *pPM)
{
	int nThreadNum = *(int *)pPM; 
	
	Sleep(50);
	EnterCriticalSection(&g_csThreadCode);
	g_nNum++;
	Sleep(0);
	printf("当前线程为:%d,当前计数为:%d\n",nThreadNum,g_nNum);
	LeaveCriticalSection(&g_csThreadCode);
	return 0;
}

windows编程之多线程总结_第5张图片线程ID不符合要求。。

使用事件的情况:(下面这里用的是手动设置事件,所以要调用ResetEvent)。
#include <stdio.h>
#include <process.h>
#include <windows.h>
long g_nNum;
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = 10;

HANDLE  g_hThreadEvent;
CRITICAL_SECTION g_csThreadCode;
int main()
{

	g_hThreadEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
	InitializeCriticalSection(&g_csThreadCode);

	HANDLE  handle[THREAD_NUM];	
	g_nNum = 0;
	int i = 0;
	while (i < THREAD_NUM) 
	{
		handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
		WaitForSingleObject(g_hThreadEvent, INFINITE); 
		ResetEvent(g_hThreadEvent);
		i++;
	}
	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
	for(i=0;i<sizeof(handle);i++)
	{
		CloseHandle(handle[i]);
	}
	CloseHandle(g_hThreadEvent);
	DeleteCriticalSection(&g_csThreadCode);
	return 0;
}
unsigned int __stdcall Fun(void *pPM)
{
	int nThreadNum = *(int *)pPM; 
	SetEvent(g_hThreadEvent);
	Sleep(50);
	EnterCriticalSection(&g_csThreadCode);
	g_nNum++;
	Sleep(0);
	printf("当前线程为:%d,当前计数为:%d\n",nThreadNum,g_nNum);
	LeaveCriticalSection(&g_csThreadCode);
	return 0;
}
windows编程之多线程总结_第6张图片线程ID符合要求。。

互斥量

#include <stdio.h>
#include <process.h>
#include <windows.h>

long g_nNum;
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = 10;

HANDLE  g_mutex;


int main()
{

	g_mutex = CreateMutex(NULL, FALSE, NULL);
	HANDLE  handle[THREAD_NUM];	
	g_nNum = 0;	
	int i = 0;
	while (i < THREAD_NUM) 
	{
		handle[i++] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL); 
		
	}
	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);

	CloseHandle(g_mutex);
	
	for (i = 0; i < THREAD_NUM; i++)
		CloseHandle(handle[i]);
	return 0;
}
unsigned int __stdcall Fun(void *pPM)
{
	Sleep(50);
	WaitForSingleObject(g_mutex,INFINITE);
	g_nNum++;
	Sleep(0);
	printf("当前计数为:%d\n",g_nNum);
	ReleaseMutex(g_mutex);
	return 0;
}

windows编程之多线程总结_第7张图片

信号量(Semaphore)

HANDLE CreateSemaphore(

  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,

  LONG lInitialCount,

  LONG lMaximumCount,

  LPCTSTR lpName

);------------------创建信号量

HANDLE OpenSemaphore(

  DWORD dwDesiredAccess,

  BOOL bInheritHandle,

  LPCTSTR lpName

);------------------打开信号量

BOOL ReleaseSemaphore(

  HANDLE hSemaphore,

  LONG lReleaseCount,  

  LPLONG lpPreviousCount 

);--------------------释放信号量


信号量的增: ReleaseSemaphore。信号量的减:WaitForSingleObject(..);当请求信号量时,如果当前信号量为0即不触发状态,那么线程进入阻塞状态,直到信号量值>0,该函数才返回,线程进入可调度状态,同时信号量值减一。

注意:信号量的值不可能小于0。


#include <stdio.h>
#include <process.h>
#include <windows.h>
long g_nNum;
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = 10;

HANDLE            g_Semaphore;
CRITICAL_SECTION  g_CriticalSection;
int main()
{
	
	
	g_Semaphore = CreateSemaphore(NULL, 0, 1, NULL);
	InitializeCriticalSection(&g_CriticalSection);

	HANDLE  handle[THREAD_NUM];	
	g_nNum = 0;
	int i = 0;
	while (i < THREAD_NUM) 
	{
		handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
		WaitForSingleObject(g_Semaphore, INFINITE);
		++i;
	}
	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);

	
	DeleteCriticalSection(&g_CriticalSection);
	CloseHandle(g_Semaphore);
	for (i = 0; i < THREAD_NUM; i++)
		CloseHandle(handle[i]);
	return 0;
}
unsigned int __stdcall Fun(void *pPM)
{
	int nThreadNum = *(int *)pPM;
	ReleaseSemaphore(g_Semaphore, 1, NULL);

	Sleep(50);

	EnterCriticalSection(&g_CriticalSection);
	++g_nNum;
	Sleep(0);
	printf("当前线程为:%d,当前计数为:%d\n",nThreadNum,g_nNum);
	LeaveCriticalSection(&g_CriticalSection);
	return 0;
}

windows编程之多线程总结_第8张图片

你可能感兴趣的:(多线程,信号量,互斥,原子锁)