win32核心编程02:线程同步

临界区域:多个线程都可访问的数据或者代码

数据脏:多个线程同时访问临界区,有可能导致代码出错或者数据混乱。

举例:

#include "pch.h"
#include <Windows.h>
#include <cstdio>
#include <iostream>
using namespace std;
int n = 0;
DWORD WINAPI func1() {
	for (int i = 0; i < 500000; i++) {
		n++;
	}
	return 0;
}
DWORD WINAPI func2() {
	for (int i = 0; i < 500000; i++) {
		n++;
	}
	return 0;
}
int main()
{
	char m[1024] = "xidian university";
	HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func1,NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func2, NULL, NULL, NULL);
	WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h2, INFINITE);
	printf("n == %d\n", n);
	while (1);
	return 0;
}

实验结果和原因分析见:https://blog.csdn.net/weixin_43415644/article/details/99940624

0x00 解决临界区数据脏的问题:

线程同步:所谓线程同步,就是让多个线程在争用资源时不会出问题。

操作系统的7种状态:

用户态:同一个进程中的线程并发。

  1. 原子锁:Interlocked
  2. 读写锁:SRWLock
  3. 临界区:CriticalSection

内核态:跨进程的线程并发

  1. 事件Event
  2. 互斥Mutex
  3. 旗语(信号量) Semaphore

First.用户态:实现同一进程的不同线程同步

0x01 原子锁:

每一步操作都是原子操作(不可分割)。

例如:

InterlockedAnd 与

InterlockedOr 或

InterlockedXor 异或

InterlockedIncrement 自增

InterlockedDecrement 自减

InterlockedAdd 加

将n++修改为InterlockedIncrement即可

#include "pch.h"
#include <Windows.h>
#include <cstdio>
#include <iostream>
using namespace std;
int n = 0;
DWORD WINAPI func1() {
	for (int i = 0; i < 500000; i++) {
		//n++;
		InterlockedIncrement((unsigned long long*)&n);
	}
	return 0;
}
DWORD WINAPI func2() {
	for (int i = 0; i < 500000; i++) {
		//n++;
		InterlockedIncrement((unsigned long long*)&n);
	}
	return 0;
}
int main()
{
	char m[1024] = "xidian university";
	HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func1, NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func2, NULL, NULL, NULL);
	WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h2, INFINITE);
	printf("n == %d\n", n);
	while (1);
	return 0;
}

0x02 临界区

1.创建临界区:创建一个CRITICAL_SECTION类型的 变量

2.初始化临界区:给创建好CRITICAL_SECTION变量赋值

3.使用临界区

           3.1 进入临界区:EnterCriticalSection()

           3.2离开临界区:LeaveCriticalSection()

4.删除临界区:DeleteCriticalSection()

临界区如何解决临界区域数据脏的问题呢?在一个线程进入临界区之后,离开临界区之前,其他线程无权读写这一个内存区域。

#include "pch.h"
#include <Windows.h>
#include <cstdio>
#include <iostream>
using namespace std;
int n = 0;
CRITICAL_SECTION section;
DWORD WINAPI func1() {

	for (int i = 0; i < 500000; i++) {
		EnterCriticalSection(&section);
		n++;//各种操作
		LeaveCriticalSection(&section);

	}
	return 0;
}
DWORD WINAPI func2() {
	for (int i = 0; i < 500000; i++) {
		EnterCriticalSection(&section);
		n++;
		LeaveCriticalSection(&section);
	}
	return 0;
}
int main()
{
	InitializeCriticalSection(&section);
	HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func1, NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func2, NULL, NULL, NULL);
	WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h2, INFINITE);
	printf("n == %d\n", n);
	DeleteCriticalSection(&section);
	while (1);
	return 0;
}

0x03 读写锁:

读读相容:对于同一段内存,一个线程进行读操作的时候,另一个线程不能进行读操作。

读写相斥:对于同一段内存,一个线程进行读(写)操作的时候,另一个线程不能进行写(读)操作。

写写相斥:对于同一段内存,一个线程进行写操作的时候,另一个线程不能进行写操作。

如何使用?

  1. 创建读写锁:
  2. 初始化读写锁:
  3. 使用读写锁:
    1. 请求读锁:AcquireSRWLockShared
    2. 释放读锁:ReleaseSRWLockShared
    3. 请求写锁:AcquireSRWLockExclusive
    4. 释放写锁:ReleaseSRWLockExclusive

例如:

读读相容:读锁和读锁是相容的,两把读锁可以同时操作同一段内存

#include "pch.h"
#include <Windows.h>
#include <cstdio>
#include <iostream>
using namespace std;
int n = 0;
SRWLOCK srwLock;
DWORD WINAPI func2() {
	for (int i = 0; i < 500000; i++) {
		AcquireSRWLockShared(&srwLock);
		n++;
		ReleaseSRWLockShared(&srwLock);
	}
	return 0;
}

DWORD WINAPI func3() {
	for (int i = 0; i < 500000; i++) {
		AcquireSRWLockShared(&srwLock);
		n++;
		ReleaseSRWLockShared(&srwLock);
	}
	return 0;
}
int main()
{
	InitializeSRWLock(&srwLock);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func2, NULL, NULL, NULL);
	HANDLE h3 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func3, NULL, NULL, NULL);
	WaitForSingleObject(h2, INFINITE);
	WaitForSingleObject(h3, INFINITE);
	printf("n == %d\n", n);
	
	while (1);
	return 0;
}

读写相斥:

读锁和写锁不能同时对同一段内存进行操作。

#include "pch.h"
#include <Windows.h>
#include <cstdio>
#include <iostream>
using namespace std;
int n = 0;
SRWLOCK srwLock;
DWORD WINAPI func1() {

	for (int i = 0; i < 500000; i++) {
		
		//EnterCriticalSection(&section);
		n++;//各种操作
		//LeaveCriticalSection(&section);

	}
	return 0;
}
DWORD WINAPI func2() {
	for (int i = 0; i < 500000; i++) {
		AcquireSRWLockShared(&srwLock);
		n++;
		ReleaseSRWLockShared(&srwLock);
	}
	return 0;
}

DWORD WINAPI func3() {
	for (int i = 0; i < 500000; i++) {
		AcquireSRWLockExclusive(&srwLock);
		n++;
		ReleaseSRWLockExclusive(&srwLock);
	}
	return 0;
}
int main()
{
	InitializeSRWLock(&srwLock);
	char m[1024] = "xidian university";
	//HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func1, NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func2, NULL, NULL, NULL);
	HANDLE h3 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func3, NULL, NULL, NULL);
	//WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h2, INFINITE);
	WaitForSingleObject(h3, INFINITE);
	printf("n == %d\n", n);
	
	while (1);
	return 0;
}

执行结果:n == 1000000

写写相斥:

#include "pch.h"
#include <Windows.h>
#include <cstdio>
#include <iostream>
using namespace std;
int n = 0;
SRWLOCK srwLock;
DWORD WINAPI func1() {

	for (int i = 0; i < 500000; i++) {
		
		//EnterCriticalSection(&section);
		n++;//各种操作
		//LeaveCriticalSection(&section);

	}
	return 0;
}
DWORD WINAPI func2() {
	for (int i = 0; i < 500000; i++) {
		AcquireSRWLockExclusive(&srwLock);
		n++;
		ReleaseSRWLockExclusive(&srwLock);
	}
	return 0;
}

DWORD WINAPI func3() {
	for (int i = 0; i < 500000; i++) {
		AcquireSRWLockExclusive(&srwLock);
		n++;
		ReleaseSRWLockExclusive(&srwLock);
	}
	return 0;
}
int main()
{
	InitializeSRWLock(&srwLock);
	char m[1024] = "xidian university";
	//HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func1, NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func2, NULL, NULL, NULL);
	HANDLE h3 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func3, NULL, NULL, NULL);
	//WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h2, INFINITE);
	WaitForSingleObject(h3, INFINITE);
	printf("n == %d\n", n);
	
	while (1);
	return 0;
}

选择顺序:

如果每个线程进行都是简单操作,直接选择使用原子锁

如果每个线程进行是比较复杂的数据操作,选择读写锁,更加灵活

如果不是数据操作,而是一段代码。选择临界区更好一些。

Secondly:内核态——实现不同进程之间的线程同步。

0x01 事件:

操作步骤:

  1. 创建事件:CreateEvent
    CreateEventA(
        _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,//事件属性,一般写NULL表示默认
        _In_ BOOL bManualReset,//是否重新设置
        _In_ BOOL bInitialState,//初始化状态
        _In_opt_ LPCSTR lpName//事件的名字
        );

     

  2. 打开事件:OpenEvent
  3. 设置事件:SetEvent
  4. 重置事件:ResetEvent

例如:

f1函数和f2函数都在等待信号,只有等待到信号才会执行之后的语句。f3()函数用来每隔两秒发送一次信号。Reset函数用来杀死信号。

// 02-内核态线程同步.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <Windows.h>
using namespace std;
HANDLE hEvent;
void f1() {
	
	while (1) {
		WaitForSingleObject(hEvent, INFINITE);
		cout << "线程1" << endl;
		ResetEvent(hEvent);
	}
}
void f2()
{

	while (1) {
		WaitForSingleObject(hEvent, INFINITE);
		cout << "线程2" << endl;
		ResetEvent(hEvent);
	 }
}
void f3() {
	while (1) {
		SetEvent(hEvent);
		Sleep(2000);
	}
}

int main() {
	hEvent = CreateEvent(NULL, false, true, TEXT("Event1"));
	HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)f1, NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)f2, NULL, NULL, NULL);
	f3();
	WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h1, INFINITE);
	return 0;
}

0x02 互斥

1.创建互斥量:CreateMutex

2.释放互斥量:ReleaseMutex

多个线程会争夺互斥量,只有一个线程可以争的到互斥量。如果这个线程不释放互斥量,那么它将一直占有。

// 02-内核态线程同步.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <Windows.h>
using namespace std;
HANDLE hMutex;
void f1() {
	
	while (1) {
		WaitForSingleObject(hMutex, INFINITE);
		cout << "线程1" << endl;
		//ReleaseMutex(hMutex);
	}
}
void f2()
{

	while (1) {
		WaitForSingleObject(hMutex, INFINITE);
		cout << "线程2" << endl;
		ReleaseMutex(hMutex);
	 }
}
int main() {
	hMutex = CreateMutex(NULL, false, NULL);
	HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)f1, NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)f2, NULL, NULL, NULL);
	WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h1, INFINITE);
	return 0;
}

0x03 旗语

  1. 创建旗语:CreateSemaphore,创建了一个数字,可以设置数字的值
  2. WaitForSingleObject 数字减少1之后,结果大于0,减少并立刻返回。数字减少1之后,结果小于0,直接阻塞。
  3. ReleaseSemaphore 给数字做加法。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值