l 线程的状态
A. 启动
1. CONTEXT
2. 使用计数 = 2
3. 暂停计数 = 1 (为0时可进入CPU的调度,当前线程是可执行的状态)
B. 运行: CPU调度
1. 执行我们的函数
a. 时不时切换线程 –> 将数据写入 CONTEXT
b. 读取CONTEXT
C. 挂起: 暂停线程的运行
1. SuspendThread函数可使线程挂起,暂停计数被+1
2. ResumeThread函数会将暂停计数-1。
3. Remark:
a. SuspendThread函数为非实时的,他只是提出需求,线程仍然会将此次时间片执行完,才挂起
b. 多次SuspendThread,就需要多次ResumeThread才能将暂停计数变为0,线程才能继续运行。
c. 一个良好的程序,不应该将线程挂起,因为你无法确定是否需要执行完当前时间片才挂起。
D. 等待休眠
1. Sleep(100)可将线程变为等待休眠状态,此100ms(非精准)中不再被CPU调度。
2. Sleep(-1)可将线程一直处于等待中,直到进程结束。
3. Sleep(0),放弃剩余的执行时间。
4. SwitchToThread(),可切换线程,具体切换由系统决定。
E. 消亡
1. 使用计数-1,变为1,需要调用CloseHandle函数才能将使用计数变为0,系统才会释放此线程内核对象。
2. 释放线程栈区。
3. 设置退出代码。
l CONTEXT结构体
A. 获取线程上下文:
1. GetThreadContext()函数。
2. 使用前需要设置结构体中ContextFlags的值,以便来获取哪些数据。
l CreateThread()和_beginthreadex()
A. _beginthreadex()是纯C语言的。
B. CreateThread()是Windows的API。
C. Remark:
1. C语言最开始设计的时候并没有多线程的概念,所以存在很多全局变量,这些全局变量在多线程中是非安全的。_beginthreadex函数新建的线程中在堆栈多开辟了一个空间,用以存放这些全局的变量,这样每个线程都有独立的这些东西,达到线程安全,然后调用CreateThread。所以推荐使用_beginthreadex!!当然其关闭函数为_endthreadex()来关闭线程,用以释放多分配的空间。
2. beginthreadex函数在最后会自动调用_endthreadex,无需自己调用。
l 原子操作
同一资源,在同一时间只有一个线程能够访问,属于系统级的操作。
系列函数:InterlockExchange()等等…
l 指定CPU运行程序
A. 指定进程:SetProcessAffinityMask(GetCurrentProcess(),0x1);
B. 指定线程:SetThreadAffinityMask(hThread,0x1)
C. 第二个参数的每一个bit表示一个CPU核,可指定多核运行。
l 旋转锁
使用旋转锁应该注意的地方:
1. 使用这种同步方式,我们需要保证需要同步的进程是运行在同一优先级的,如果两个线程优先级为6,另一个为4,那么优先级为4的线程不会得到等待资源的机会。
2. 应该避免资源标示和资源本身在同一告诉缓存行。
3. 应避免在单处理器的系统上使用旋转锁。
4. 上锁会导致1人做事多人围观的结果,其效果不一定有单线程快!!同时还会导致CPU运行百分百!
l 关键字volatile
A. volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象
B. 一般说来,volatile用在如下的几个地方:
1. 中断服务程序中修改的供其它程序检测的变量需要加volatile;
2. 多任务环境下各任务间共享的标志应该加volatile;
3. 存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义
l 临界区
1. InitializeCriticalSection:初始化临界区,但是此函数返回值为void,无法直接判断是否成功,如果失败,其会抛出一个异常。
2. InitializeCriticalSectionAndSpinCount:初始化临界区,同时设置进入临界区的测试次数,返回值为BOOL。可利用此函数来初始化,解决上个函数的问题。
3. DeleteCriticalSection:删除临界区。
4. EnterCriticalSection:进入临界区,如果无法进入,则线程进入等待状态。
5. TryEnterCriticalSection:尝试进入临界区,可通过其返回值来判断是否可进入,这样就可以做许多的事情了,而不是傻傻的等待。
6. LeaveCriticalSection:离开临界区。
l Silm锁
A. InitializeSRWLock:初始化silm锁,无需delete。
B. 以独占的方式进入:
C. 以共享的方式进入:
D. 缺点:
1. 无法调度,没有try类型的函数。
2. 无法使用递归,但临界区可以递归。