0%

Windows核心编程 - 用户方式中线程同步

最近在学习 Win32编程,所以顺便将每日所学记录下来,一方面为了巩固学习的知识,另一方面也为同样在学习Win32开发的童鞋们提供一份参考。

本系列博文均根据学习《Windows核心编程》一书总结而来;

运行环境:

  • 操作系统: Windows 10家庭版
  • 编译器:Visual Studio 2019

用户方式下线程同步

当所有线程在互相之间不需要进行通信的情况下就可以顺利运行时,Windows的运行性能最好。但是线程很少能够独立进行操作。

线程需要在下面两种情况需要进行通信:

  • 当有多个线程访问共享资源而不使资源被破坏时;
  • 当一个线程需要将某个任务已经完成的情况通知另外一个或多个线程时;

原子锁

线程同步问题再很大程度上与原子访问有关,所谓源自访问,是指线程在访问资源时能够确保所有其他线程都不在同一时间内访问相同资源;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
int g_x = 0; //定义全局变量

DWORD WINAPI ThreadProc1(LPVOID pR)
{
g_x++;

printf("FirstThread g_x = %d\n", g_x);
return 0;
}
DWORD WINAPI ThreadProc2(LPVOID pR)
{
g_x++;
printf("SecondThread g_x = %d\n", g_x);
return 0;
}

int main()
{
HANDLE hThread[2];
hThread[0] = CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);
hThread[1] = CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
getchar();
return 0;
}

因为Windows是抢占式操作系统,所以线程并不一定会按照我们期许的那样执行,有可能执行完结果为1,因为我们不知道CPU在什么会中断我们的线程;

我们需要一种手段来保证值的递增能够以原子操作来进行,也就是不中断运行;

1
2
LONG InterlockedExchangeAdd (  LPLONG volatile Addend,  // addend
LONG Value // increment value);

修改代码为以下的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <process.h>
LONG g_x = 0; //这里需要定义为长整型

DWORD WINAPI ThreadProc1(LPVOID pR)
{
InterlockedExchangeAdd(&g_x, 1);

printf("FirstThread g_x = %d\n", g_x);
return 0;
}
DWORD WINAPI ThreadProc2(LPVOID pR)
{
InterlockedExchangeAdd(&g_x, 1);
printf("SecondThread g_x = %d\n", g_x);
return 0;
}

int main()
{
HANDLE hThread[2];
hThread[0] = CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);
hThread[1] = CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
getchar();
return 0;
}

互斥锁是如何运行的,对于x86平台来说,互锁函数会对总线发出一个硬件信号,防止另一个CPU访问同一个内存地址。

也可以使用InterlockedExchange 或InterlockedExchangePointer函数:

1
2
3
4
5
6
7
PVOID InterlockedExchangePointer(
PVOID volatile *Target, // value to exchange
PVOID Value // new value);

LONG InterlockedExchange(
LPLONG Target,
LONG Value );

临界区同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <Windows.h>
#include <tchar.h>
#include <stdio.h>
int g_dwTickets = 10;
CRITICAL_SECTION cs; //创建全局临界资源

DWORD WINAPI MyFirstThread1(LPVOID lpParameter)
{
EnterCriticalSection(&cs);
while (g_dwTickets > 0)
{
printf("剩余:%d\n", g_dwTickets);
g_dwTickets--;
printf("卖出一张剩余:%d\n", g_dwTickets);
}
LeaveCriticalSection(&cs);
return 0;
}


DWORD WINAPI MyFirstThread2(LPVOID lpParameter)
{

return 0;
}

int main()
{
InitializeCriticalSection(&cs);
DWORD dwResult1;
DWORD dwResult2;
HANDLE aThreadHandles[2];
aThreadHandles[0] = CreateThread(NULL,
0,
MyFirstThread1,
NULL,
0,
NULL
);
aThreadHandles[1] = CreateThread(NULL,
0,
MyFirstThread1,
NULL,
0,
NULL
);
WaitForMultipleObjects(2, aThreadHandles, TRUE, INFINITE);
printf("线程执行完成\n");
DWORD dwResults[2];
GetExitCodeThread(aThreadHandles[0], &dwResults[0]);
GetExitCodeThread(aThreadHandles[1], &dwResults[1]);
printf("%d %d ", dwResults[1], dwResults[2]);
getchar();
return 0;
}