控制线程
线程安全问题
临界区的设计思路:
线程安全问题的产生,我认为主要是不能够确定两个或多个线程操作同一资源时(全局变量),是否已经操作完毕,而临界区的设计,就像要去上公共厕所的两个人,一个人进去上厕所时会锁门(发布一个令牌),另一个人去上厕所时因为门锁了就进不去了,只能等第一个人出来才能进去。
这样确定了一个线程执行操作代码完后另一个才能操作,不会出现一个线程要取出一个数 ++,结果刚取出这个数还没++,时间片已经结束(20ms),另一个线程也要取出这个数 ++,那么两个线程都 ++完后,实际上得到的数只加了一次,这不是我们想要的结果
临界区就像把一堆代码封装成一句代码.
临界区使用:
1、创建CRITICAL_SECTION:
CRITICAL_SECTION cs;
2、在使用前进行初始化
InitializeCriticalSection(&cs);
3、在函数中使用:
4、删除CRITICAL_SECTION
VOID DeleteCriticalSection(PCRITICAL_SECTION pcs);
当线程不再试图访问共享资源时
怎么使用是合理的?
应当是操作一个全局变量,就定义一个令牌。
且该令牌的作用域仅停留在操作该全局变量的代码
产生死锁?
X,Y是两个线程 A,B是两个令牌
这样的代码是安全的:
而上面的代码不安全
当X线程执行到红色区域时,时间片(20s)结束,然后Y线程执行,拿走了令牌B
出现了X等着令牌B,Y等着令牌A,两个线程都无法执行,互相等待
阻塞(等待)函数
功能说明:
等待函数可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止.
hHandle:
内核对象句柄,可以是进程也可以是线程.
dwMilliseconds:
等待时间,单位是毫秒 INFINITE(-1)一直等待
返回值:
WAIT_OBJECT_0(0) 等待对象变为已通知
WAIT_TIMEOUT(0x102) 超时
特别说明:
1、内核对象中的每种对象都可以说是处于已通知或未通知的状态之中
2、这种状态的切换是由Microsoft为每个对象建立的一套规则来决定的
3、当线程正在运行的时候,线程内核对象处于未通知状态
4、当线程终止运行的时候,它就变为已通知状态
5、在内核中就是个BOOL值,运行时FALSE 结束TRUE
群体阻塞(等待)函数
DWORD WaitForMultipleObjects(
DWORD nCount, // number of handles in array
CONST HANDLE *lpHandles, // object-handle array
BOOL bWaitAll, // wait option
DWORD dwMilliseconds // time-out interval
);
互斥体(Mutex)
创建互斥体:
HANDLE g_hMutex = CreateMutex(NULL,FALSE, "XYZ");
临界区的设计其实就是一种互斥
WaitForSingleObject(g_hMutex,INFINITE); //等待(阻塞)函数 第二个参数:等待时间
INFINITE(宏) == -1: 为一直等待
此时如果互斥体处于已通知状态,则停止阻塞,将互斥体转为未通知状态,并且该线程向后执行,获得控制权
而如果互斥体处于未通知状态,则一直阻塞直到转为互斥体转为未通知状态
//逻辑代码
ReleaseMutex(g_hMutex);
使互斥体回到已通知状态,其他线程可以获得其控制权
互斥体是一种内核对象
HANDLE g_hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ");
//在其他进程访问该互斥体
事件
事件是一种内核对象(内核对象一旦创建,是在高2G内存空间中)
创建事件对象
HANDLE g_hEvent = CreateEvent(NULL, TRUE, FALSE, "XYZ");
参数说明:
第一个参数是安全属性,第二个参数是需要手动设置状态,即wait函数执行后不会改变通知状态,如果是false就会自动更改状态,第三个参数是通知状态初始值,false是未通知状态,true是已通知状态
事件对象的控制
BOOL SetEvent(HANDLE hEvent); //改变为已通知状态
事件内核对象的获取
HANDLE g_hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "XYZ");
信号量
关于句柄和ID
1、都是系统分配的一个编号,句柄是客户程序使用 ID主要是系统调度时使用.
2、调用CloseHandle关闭进程或者线程句柄的时候,只是让内核计数器减少一个,并不是终止进程或者线程.
进程或线程将继续运行,直到它自己终止运行。
3、进程ID与线程ID 是不可能相同。但不要通过进程或者线程的ID来操作进程或者线程,因为,这个编号是会重复使用的,也就是说,当你通过ID=100这个编号去访问一个进程的时候,它已经结束了,而且系统将这个编号赋给了另外一个进程或者线程.