多线程同步

1、建立线程windows

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;
}

运行便可知道,其不一样步。


2、多线程同步安全

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


临界区对象:函数

初始化临界区命令行

void WINAPI InitializeCriticalSection(
	_Out_  LPCRITICAL_SECTION lpCriticalSection
);


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

进入临界区函数:线程

void EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);

释放临界区:

void LeaveCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);


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

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


代码以下:code

// 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;
}
其结果以下:


能够知道,在如今多核下,以上创建临界区不能很好的同步。对象


事件对象:blog

建立一事件对象进程

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;
}

结果以下:

能够看出用事件对象能够达到同步效果,据其原理是用了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; 
}
一样也能完成线程同步