程序是计算机指令的集合,它以文件的形式存储在磁盘上。而进程通常被定义为一个正在运行程序的实例,是一个程序在其自身地址空间的依次执行活动。
进程:由操作系统用来管理进程的内核对象和地址空间组成(4GB)
线程:由线程的内核对象和线程栈组成
当多线程访问全局变量时需要多线程同步:互斥对象、事件对象、关键代码段
互斥对象:互斥独享可以看做是一把房间钥匙,只有得到这把钥匙后,我们才能进入放进啊,完成工作,我进入房间后,要是在我手上,其他人拿不到,因此无法进入房间,只能等待,等我离开房间交出钥匙,其他人才能进入房间。
几个注意的地方:
(1)互斥量为内核对象,能够与其他线程或特殊事件取得同步;
(2)速度比临界区要慢;
(3)互斥量对象与所有其它内核对象的不同之处在于它是被线程所拥有的,互斥量对象除了记录当前信号状态外,还要记住此时那个线程拥有它。
(4)这个常来被运用于限制程序启动次数!
<span style="font-family: Arial, Helvetica, sans-serif;">#include <Windows.h> </span>
#include <iostream>
using namespace std;
DWORD WINAPI Funproc1(LPVOID Lp1);
DWORD WINAPI Funproc2(LPVOID Lp2);
int tickets=100;
HANDLE HMutex;
void main()
{
//默认安全性;0默认使用与调用该函数线程相同的栈空间大小;入口函数;初始化传递参数的值;0线程创建后立即运行;接收线程ID
HANDLE HThread1=CreateThread(NULL,0,Funproc1,NULL,0,NULL);
HANDLE HThread2=CreateThread(NULL,0,Funproc2,NULL,0,NULL);
CloseHandle(HThread1);
CloseHandle(HThread2);
//创建互斥对象
//1、默认安全性、2、为true创建互斥对象的线程拥有该对象的所有权,否则该线程将不获得所创建的互斥对象的所有权,3、制定互斥对象的名称
HMutex=CreateMutex(NULL,FALSE,NULL);
Sleep(1000);
}
DWORD WINAPI Funproc1(LPVOID Lp1)
{
while(true)
{
//1、所请求的对象句柄,2、制定等待的时间间隔,为0,立即返回,INFINITE函数永远等待,直到等待的对象处于有信号状态
WaitForSingleObject(HMutex,INFINITE);
if(tickets>0)
{
Sleep(1);
cout<<"thread1 sell tickets:"<<tickets--<<endl;
}
else
break;
ReleaseMutex(HMutex);
}
return 0;
}
DWORD WINAPI Funproc2(LPVOID Lp2)
{
while(true)
{
//1、所请求的对象句柄,2、制定等待的时间间隔,为0,立即返回,INFINITE函数永远等待,直到等待的对象处于有信号状态
WaitForSingleObject(HMutex,INFINITE);
if(tickets>0)
{
Sleep(1);
cout<<"thread2 sell tickets:"<<tickets--<<endl;
}
else
break;
ReleaseMutex(HMutex);
}
return 0;
}
事件对象
:属于内核对象当人工重置的对象得到通知时,等待事件对象的所有线程均变为可调度线程,当一个自动重置对象得到通知时
等待该事件对象的线程只有一个线程变为可调度线程。要想实现线程同步,应该使用自动重置对象,同时操作系统会将该事件对象设置无信号状态,所以执行完保护代码后要SetEvent重置信号。
几个注意的地方:
(1).和Mutex使用差不多,只有细微的差别;
(2).可以使用SetEvent或ResetEvent改变其状态;
(3).在应用程序中任意一处没有正确的按照规则调用SetEvent或ResetEvent,将达不到同步或互斥的目的;
(4).一般来说,都是利用Event来进行同步,而不是我们这里的让它来达到互斥;
(5).Event处于无信号状态时,相关线程或进程退出,系统并不会尝试将其置为有信号状态;
#include <Windows.h>
#include <iostream>
using namespace std;
DWORD WINAPI Funproc1(LPVOID Lp1);
DWORD WINAPI Funproc2(LPVOID Lp2);
int tickets=100;
HANDLE HEvent;
void main()
{
//1、默认安全性2、为FALSE创建一个人工重置对象3、无信号4、事件对象的名称
HEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
SetEvent(HEvent);
//默认安全性;0默认使用与调用该函数线程相同的栈空间大小;入口函数;初始化传递参数的值;0线程创建后立即运行;接收线程ID
HANDLE HThread1=CreateThread(NULL,0,Funproc1,NULL,0,NULL);
HANDLE HThread2=CreateThread(NULL,0,Funproc2,NULL,0,NULL);
CloseHandle(HThread1);
CloseHandle(HThread2);
//创建互斥对象
Sleep(4000);
CloseHandle(HEvent);//关闭事件对象句柄
}
DWORD WINAPI Funproc1(LPVOID Lp1)
{
while(true)
{
//1、所请求的对象句柄,2、制定等待的时间间隔,为0,立即返回,INFINITE函数永远等待,直到等待的对象处于有信号状态
WaitForSingleObject(HEvent,INFINITE);
if(tickets>0)
{
Sleep(1);
cout<<"thread1 sell tickets:"<<tickets--<<endl;
}
else
break;
SetEvent(HEvent);
}
return 0;
}
DWORD WINAPI Funproc2(LPVOID Lp2)
{
while(true)
{
//1、所请求的对象句柄,2、制定等待的时间间隔,为0,立即返回,INFINITE函数永远等待,直到等待的对象处于有信号状态
WaitForSingleObject(HEvent,INFINITE);
if(tickets>0)
{
Sleep(1);
cout<<"thread2 sell tickets:"<<tickets--<<endl;
}
else
break;
SetEvent(HEvent);
}
return 0;
}
关键代码段;也成为临界区,工作在用户方式下,它是指一个小代码段,在代码能够执行前,他必须独占对某些资源的访问权
几个注意的地方:
(1).比Mutex速度快;
(2).临界区在线程内的分配必须是全局的;
(3). 临界区一次只允许一个线程访问;
<span style="font-family: Arial, Helvetica, sans-serif;">#include <Windows.h> </span>
#include <iostream>
using namespace std;
DWORD WINAPI Funproc1(LPVOID Lp1);
DWORD WINAPI Funproc2(LPVOID Lp2);
int tickets=100;
CRITICAL_SECTION g_cs;//一个结构体
void main()
{
//默认安全性;0默认使用与调用该函数线程相同的栈空间大小;入口函数;初始化传递参数的值;0线程创建后立即运行;接收线程ID
HANDLE HThread1=CreateThread(NULL,0,Funproc1,NULL,0,NULL);
HANDLE HThread2=CreateThread(NULL,0,Funproc2,NULL,0,NULL);
CloseHandle(HThread1);
CloseHandle(HThread2);
//创建互斥对象
InitializeCriticalSection(&g_cs);//创建临界区对象(指向结构体的指针)
Sleep(4000);
DeleteCriticalSection(&g_cs);//释放没有被任何线程使用的临界区对象的所有资源
}
DWORD WINAPI Funproc1(LPVOID Lp1)
{
while(true)
{
//判断能否得到指定临界区的对象的所有权,如果不能得到就一直等待,得到后访问受保护的资源,访问完成后释放指定的临界区的对象的所有权
EnterCriticalSection(&g_cs);
if(tickets>0)
{
Sleep(1);
cout<<"thread1 sell tickets:"<<tickets--<<endl;
}
else
break;
//访问完成后释放指定的临界区的对象的所有权
LeaveCriticalSection(&g_cs);
}
return 0;
}
DWORD WINAPI Funproc2(LPVOID Lp2)
{
while(true)
{
EnterCriticalSection(&g_cs);
if(tickets>0)
{
Sleep(1);
cout<<"thread2 sell tickets:"<<tickets--<<endl;
}
else
break;
LeaveCriticalSection(&g_cs);
}
return 0;
}