C++实现线程同步的四种方式

2023-04-02 10:18:41 2883人已围观 48已点赞 4人已收藏

简介本文介绍C++实现线程同步的四种方式:事件对象、互斥对象、临界区、信号量,感兴趣的朋友可以参考一下。

什么是线程同步和互斥

同步就是协同步调,按预定的先后次序进行运行 。如:你说完,我再说。这里的同步千万不要理解成那个同时进行,应是指协同、协助、互相配合。线程同步是指多线程通过特定的设置(如互斥量,事件对象,临界区)来控制线程之间的执行顺序(即所谓的同步)也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间是各自运行各自的!

线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性 。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。

事件对象

通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作。

总结

  • 事件是内核对象,事件分为手动置位事件和自动置位事件。事件Event内部它包含一个使用计数(所有内核对象都有),一个布尔值表示是手动置位事件还是自动置位事件,另一个布尔值用来表示事件有无触发。
  • 事件可以由SetEvent()来触发,由ResetEvent()来设成未触发。还可以由PulseEvent()来发出一个事件脉冲。
  • 事件可以解决线程间同步问题,因此也能解决互斥问题。

示例代码:

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

int WorkerID = 10;
const int MAXWORKERID = 100;
//Declare event handle
HANDLE hEvent;

//Define thread function
DWORD __stdcall ThreadFunOne(LPVOID lParam)
{
	for (;;)
	{
		WaitForSingleObject(hEvent, INFINITE);


		if (WorkerID < MAXWORKERID)
		{
			WorkerID += 1;
			Sleep(1000);
			printf("ThreadOne print out: %i \n", WorkerID);
		}
		SetEvent(hEvent);
	}
	return 0;
}

DWORD __stdcall ThreadFunTwo(LPVOID lParam)
{
	for (;;)
	{
		WaitForSingleObject(hEvent, INFINITE);
		if (WorkerID < MAXWORKERID)
		{
			WorkerID += 1;
			Sleep(1000);
			printf("ThreadTwo print out: %i \n", WorkerID);
		}
		SetEvent(hEvent);
	}
	return 0;
}

int main()
{
	//Define thread handle
	HANDLE hThread1, hThread2;
	//Create thread
	hThread1 = ::CreateThread(NULL, 0, ThreadFunOne, NULL, 0, NULL);
	hThread2 = ::CreateThread(NULL, 0, ThreadFunTwo, NULL, 0, NULL);

	//Create event
	hEvent = CreateEvent(NULL, FALSE, TRUE, _T("event"));
	//Close thread handle
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	//Note: Prevent process exiting
	while (true)
	{
		;
	}
	return 0;
}

输出:

C++实现事件对象,互斥对象,临界区,信号量

互斥对象

互斥对象和临界区很像,采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程同时访问。当前拥有互斥对象的线程处理完任务后必须将线程交出,以便其他线程访问该资源。

总结

  • 互斥量是内核对象,它与关键段都有“线程所有权”所以不能用于线程的同步。
  • 互斥量能够用于多个进程之间线程互斥问题,并且能完美的解决某进程意外终止所造成的“遗弃”问题。

示例代码:

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

int WorkerID = 10;
const int MAXWORKERID = 50;
//Declare mutex handle
HANDLE hMutex;

//Define thread function
DWORD __stdcall ThreadFunOne(LPVOID lParam)
{
	for (;;)
	{
		WaitForSingleObject(hMutex, INFINITE);
		if (WorkerID < MAXWORKERID)
		{
			WorkerID += 1;
			Sleep(10);
			printf("ThreadOne print out: %i \n", WorkerID);
		}
		ReleaseMutex(hMutex);
	}
	return 0;
}

DWORD __stdcall ThreadFunTwo(LPVOID lParam)
{
	for (;;)
	{
		WaitForSingleObject(hMutex, INFINITE);
		if (WorkerID < MAXWORKERID)
		{
			WorkerID += 1;
			printf("ThreadTwo print out: %i \n", WorkerID);
			Sleep(1000);
		}
		ReleaseMutex(hMutex);
	}
	return 0;
}

int main()
{
	//Define thread handle
	HANDLE hThread1, hThread2;
	//Create thread
	hThread1 = ::CreateThread(NULL, 0, ThreadFunOne, NULL, 0, NULL);
	hThread2 = ::CreateThread(NULL, 0, ThreadFunTwo, NULL, 0, NULL);

	//Create mutex
	hMutex = CreateMutex(NULL, false, _T("mutex"));
	//Close thread handle
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	//Note: Prevent process exiting
	while (true)
	{
		;
	}
	return 0;
}

输出:

C++实现事件对象,互斥对象,临界区,信号量

临界区

通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。它并不是核心对象,不是属于操作系统维护的,而是属于进程维护的。

总结:

  • 关键段共初始化化、销毁、进入和离开关键区域四个函数。
  • 关键段可以解决线程的互斥问题,但因为具有“线程所有权”,所以无法解决同步问题。
  • 推荐关键段与旋转锁配合使用。

示例代码:

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

int WorkerID = 10;
const int MAXWORKERID = 100;
//Declare Critical Section 
CRITICAL_SECTION  Section;

//Define thread function
DWORD __stdcall ThreadFunOne(LPVOID lParam)
{
	for (;;)
	{
		//	EnterCriticalSection(&Section);
		if (WorkerID < MAXWORKERID)
		{
			WorkerID += 1;
			Sleep(1000);
			printf("ThreadOne print out: %i \n", WorkerID);
		}
		//	LeaveCriticalSection(&Section);
	}
	return 0;
}

DWORD __stdcall ThreadFunTwo(LPVOID lParam)
{
	for (;;)
	{
		EnterCriticalSection(&Section);
		if (WorkerID < MAXWORKERID)
		{
			WorkerID += 1;
			printf("ThreadTwo print out: %i \n", WorkerID);
			Sleep(1000);
		}
		LeaveCriticalSection(&Section);
	}
	return 0;
}

int main()
{

	//Initialize critical section
	InitializeCriticalSection(&Section);
	//Define thread handle
	HANDLE hThread1, hThread2;
	//Create thread
	hThread1 = ::CreateThread(NULL, 0, ThreadFunOne, NULL, 0, NULL);
	hThread2 = ::CreateThread(NULL, 0, ThreadFunTwo, NULL, 0, NULL);

	//Close thread handle
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	//Note: Prevent process exiting
	while (true)
	{
		;
	}
	return 0;
}

输出:

C++实现事件对象,互斥对象,临界区,信号量

信号量

信号量也是内核对象。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。

在用CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最 大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1 ,只要当前可用资源计数是大于0 的,就可以发出信号量信号。但是当前可用计数减小 到0 时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离 开的同时通过ReleaseSemaphore ()函数将当前可用资源计数加1 。在任何时候当前可用资源计数决不可能大于最大资源计数。

示例代码:

#include<windows.h>
#include<iostream>
using namespace std;

int number = 1;		// 定义全局变量
HANDLE hSemaphore;  // 定义信号量句柄

unsigned long __stdcall ThreadProc1(void* lp)
{
	long count;
	while (number < 100)
	{
		WaitForSingleObject(hSemaphore, INFINITE);  // 等待信号量为有信号状态
		cout << "thread 1 :" << number << endl;
		++number;
		Sleep(100);
		ReleaseSemaphore(hSemaphore, 1, &count);
	}

	return 0;
}

unsigned long __stdcall ThreadProc2(void* lp)
{
	long count;
	while (number < 100)
	{
		WaitForSingleObject(hSemaphore, INFINITE);  // 等待信号量为有信号状态
		cout << "thread 2 :" << number << endl;
		++number;
		Sleep(100);
		ReleaseSemaphore(hSemaphore, 1, &count);
	}

	return 0;
}

int main()
{
	hSemaphore = CreateSemaphore(NULL, 1, 100, _T("sema"));

	// Define thread handle
	HANDLE hThread1, hThread2;
	// Create thread
	hThread1 = ::CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);
	hThread2 = ::CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);

	// Close thread handle
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	// Note: Prevent process exiting
	while (true)
	{
		;
	}
	return 0;
}

输出:

C++实现事件对象,互斥对象,临界区,信号量

源码下载
  • 最近更新:   2022-06-22开发环境:   Visual Studio 2015
  • 源码大小:   1.32KB下载次数:  29 

更多为你推荐