多线程同步

一、创建线程

HANDLE WINAPI CreateThread(
	_In_opt_   LPSECURITY_ATTRIBUTES lpThreadAttributes,  //新线程安全属性
	_In_       SIZE_T dwStackSize,                        //线程初始化时地址空间大小
	_In_       LPTHREAD_START_ROUTINE lpStartAddress,     //线程函数地址
	_In_opt_   LPVOID lpParameter,                        //传递给线程的命令行参数
	_In_       DWORD dwCreationFlags,                     //线程创建后是否立即运行
	_Out_opt_  LPDWORD lpThreadId                        //线程ID号
);

下面为代码:

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

#include "stdafx.h"
#include <windows.h>


DWORD WINAPI myfun1(
  LPVOID lpParameter	
);

DWORD WINAPI myfun2(
  LPVOID lpParameter
);


int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE h1, h2;

	h1 = ::CreateThread(NULL, 0, myfun1, NULL, 0, NULL);
	printf("线程1开始运行!\r\n");

	h2 = ::CreateThread(NULL, 0, myfun2, NULL, 0, NULL);
	printf("线程2开始运行!\r\n");

	::CloseHandle(h1);
	::CloseHandle(h2);

	

	if (getchar() == 'q')
	{
		return 0;
	}
	else
	{
		::Sleep(100);
	}

	return 0;
}

DWORD WINAPI myfun1( LPVOID lpParameter )
{
	printf("线程1正在运行!\r\n");
	return 0;
}

DWORD WINAPI myfun2( LPVOID lpParameter )
{
	printf("线程2正在运行!\r\n");
	return 0;
}

运行即可知道,其不同步。


二、多线程同步

同步有多种方法,如临界区对象、事件对象、互斥对象。


临界区对象:

初始化临界区

void WINAPI InitializeCriticalSection(
	_Out_  LPCRITICAL_SECTION lpCriticalSection
);


当用户对临界区进行初始化后,程序就可以进入该临界区,并拥有其所有权,直到其释放临界区所有权后,其他程序才能进来。

进入临界区函数:

void EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);

释放临界区:

void LeaveCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);


调用DeleteCriticalSection()将临界区从内存中删除

void DeleteCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
作用防止内存错误


代码如下:

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

#include "stdafx.h"
#include <windows.h>


DWORD WINAPI myfun1(
  LPVOID lpParameter	
);

DWORD WINAPI myfun2(
  LPVOID lpParameter
);

static int a1 = 0;
CRITICAL_SECTION Section;

int _tmain(int argc, _TCHAR* argv[])
{

	InitializeCriticalSection(&Section);

	HANDLE h1, h2;
	
	h1 = ::CreateThread(NULL, 0, myfun1, NULL, 0, NULL);
	printf("线程1开始运行!\r\n");

	h2 = ::CreateThread(NULL, 0, myfun2, NULL, 0, NULL);
	printf("线程2开始运行!\r\n");

	::CloseHandle(h1);
	::CloseHandle(h2);

	::Sleep(1000);
	
	printf("正常退出程序请按'q'\r\n");

	if (getchar() == 'q')
	{
		DeleteCriticalSection(&Section);
	}
	else
	{
		return 0;
	}

	return 0;
}

DWORD WINAPI myfun1( LPVOID lpParameter )
{
	
	while (1)
	{
		EnterCriticalSection(&Section);
		a1++;
		
		if (a1 < 1000)
		{
			::Sleep(1000);
			printf("线程1正在计数%d\r\n", a1);
			LeaveCriticalSection(&Section);
		}
		else
		{
			LeaveCriticalSection(&Section);
			break;
		}	
	}
	return 0; 
}

DWORD WINAPI myfun2( LPVOID lpParameter )
{
	while (1)
	{
		EnterCriticalSection(&Section);
		a1++;
		if (a1 < 1000)
		{
			::Sleep(1000);
			printf("线程2正在计数%d\r\n", a1);
			LeaveCriticalSection(&Section);
		}
		else
		{
			LeaveCriticalSection(&Section);
			break;
		}	
	}
	return 0;
}
其结果如下:


多线程同步_第1张图片

可以知道,在现在多核下,以上建立临界区不能很好的同步。


事件对象:

创建一事件对象

HANDLE WINAPI CreateEvent(
	_In_opt_  LPSECURITY_ATTRIBUTES lpEventAttributes,
	_In_      BOOL bManualReset,                          //表示人工重置还是自动重置,true人工,false自动
	_In_      BOOL bInitialState,                         //初始状态,true有状态,false无信号状态
	_In_opt_  LPCTSTR lpName
);


请求一事件对象

DWORD WINAPI WaitForSingleObject(
	_In_  HANDLE hHandle,       //表示所等待的事件对象句柄
	_In_  DWORD dwMilliseconds  //等待时间
);

代码如下:

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

#include "stdafx.h"
#include <windows.h>


DWORD WINAPI myfun1(
  LPVOID lpParameter	
);

DWORD WINAPI myfun2(
  LPVOID lpParameter
);

int a1 = 0;
HANDLE hevent;

DWORD WINAPI WaitForSingleObject(
	_In_  HANDLE hHandle,       //表示所等待的事件对象句柄
	_In_  DWORD dwMilliseconds  //等待时间
);

HANDLE WINAPI CreateEvent(
	_In_opt_  LPSECURITY_ATTRIBUTES lpEventAttributes,
	_In_      BOOL bManualReset,                          //表示人工重置还是自动重置,true人工,false自动
	_In_      BOOL bInitialState,                         //初始状态,true有状态,false无信号状态
	_In_opt_  LPCTSTR lpName
);

int _tmain(int argc, _TCHAR* argv[])
{

	HANDLE h1, h2;
	hevent = ::CreateEvent(NULL, FALSE, false, NULL);
	::SetEvent(hevent);
	
	h1 = ::CreateThread(NULL, 0, myfun1, NULL, 0, NULL);
	printf("线程1开始运行!\r\n");

	h2 = ::CreateThread(NULL, 0, myfun2, NULL, 0, NULL);
	printf("线程2开始运行!\r\n");

	::CloseHandle(h1);
	::CloseHandle(h2);

	::Sleep(20000);
	

	return 0;
}

DWORD WINAPI myfun1( LPVOID lpParameter )
{
	
	while (1)
	{
		::WaitForSingleObject(hevent, INFINITE);
		::ResetEvent(hevent);
		
		if (a1 < 10000)
		{
			a1++;
			::Sleep(1000);
			printf("线程1正在计数%d\r\n", a1);
			::SetEvent(hevent);
		}
		else
		{
			::SetEvent(hevent);
			break;
		}	
	}
	return 0; 
}

DWORD WINAPI myfun2( LPVOID lpParameter )
{
	while (1)
	{
		::WaitForSingleObject(hevent, INFINITE);
		::ResetEvent(hevent);

		if (a1 < 10000)
		{
			a1++;
			::Sleep(1000);
			printf("线程2正在计数%d\r\n", a1);
			::SetEvent(hevent);
		}
		else
		{
			::SetEvent(hevent);
			break;
		}	
	}
	return 0;
}

结果如下:

多线程同步_第2张图片

可以看出用事件对象可以达到同步效果,据其原理是用了WaitForSingleObject,其参数设置成永远等待


互斥对象:

互斥对象不仅可以同步线程,也可以同步进程。

创建互斥对象:

HANDLE WINAPI CreateMutex(
	_In_opt_  LPSECURITY_ATTRIBUTES lpMutexAttributes, 
	_In_      BOOL bInitialOwner,                      //表示该互斥对象的拥有者,true表示创建该互斥对象的线程拥有其所有权,反之,则没
	_In_opt_  LPCTSTR lpName                           //互斥对象的名字
);

代码如下:

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

#include "stdafx.h"
#include <windows.h>


DWORD WINAPI myfun1(
  LPVOID lpParameter	
);

DWORD WINAPI myfun2(
  LPVOID lpParameter
);

int a1 = 0;
HANDLE hmutex;




int _tmain(int argc, _TCHAR* argv[])
{

	HANDLE h1, h2;
	hmutex = ::CreateMutex(NULL, FALSE, NULL);
	
	h1 = ::CreateThread(NULL, 0, myfun1, NULL, 0, NULL);
	printf("线程1开始运行!\r\n");

	h2 = ::CreateThread(NULL, 0, myfun2, NULL, 0, NULL);
	printf("线程2开始运行!\r\n");

	::CloseHandle(h1);
	::CloseHandle(h2);

	::Sleep(20000);
	
	return 0;
}

DWORD WINAPI myfun1( LPVOID lpParameter )
{
	
	while (1)
	{
		::WaitForSingleObject(hmutex, INFINITE);
		if (a1 < 10000)
		{
			a1++;
			::Sleep(1000);
			printf("线程1正在计数%d\r\n", a1);
			::ReleaseMutex(hmutex);
		}
		else
		{
			::ReleaseMutex(hmutex);
			break;
		}	
	}
	return 0; 
}

DWORD WINAPI myfun2( LPVOID lpParameter )
{
	while (1)
	{
		::WaitForSingleObject(hmutex, INFINITE);
		if (a1 < 10000)
		{
			a1++;
			::Sleep(1000);
			printf("线程2正在计数%d\r\n", a1);
			::ReleaseMutex(hmutex);
		}
		else
		{
			::ReleaseMutex(hmutex);
			break;
		}	
	}
	return 0; 
}

同样也能完成线程同步





你可能感兴趣的:(多线程同步)