关于 Windows® CE 系统中同步机制的思考

田海立

2006-1-18

摘要

本文讨论的话题是MSDN文档没有描述或者从它里面不容易看出来的Windows® CE线程同步的问题。既然是这样,而且笔者也没有内幕资料,只能用事实说话了——用实际程序验证。文中验证了WinCE的同步机制临界区、互斥体、信号量、事件和消息队列中的一些问题,并总结出了应用它们要注意的地方。

关键字:同步,临界区,信号量,消息队列,饥饿,死锁
Keywords: Synchronization, Critical Section, Semaphore, Message Queue, Starving, Dead-lock

适用范围:本文适用于WinCE.net 4.0及以上版本,文中的程序实例在WinCE5.0 Platform Builder Emulator环境下得以验证。

知识准备:操作系统,UMLetc.

前期阅读:Windows® CE 系统中的同步机制

目 录

摘要... 1
目 录... 1
1. 前言... 2
2. 临界区(CriticalSection)... 3
    2.1 问题的提出... 3
    2.2 验证程序一... 3
        2.2.1 程序设计[程序-2-1] 3
        2.2.2 Log信息[Log-2-1] 3
    2.3 验证程序二... 4
        2.3.1 程序设计[程序-2-2] 4
        2.3.2 Log信息[Log-2-2] 4
    2.4 结果分析... 5
3. 互斥体(Mutex)... 5
    3.1 问题的提出... 5
    3.2 验证程序一... 6
        3.2.1 程序设计[程序-3-1] 6
        3.2.2 Log信息[Log-3-1] 6
    3.3 验证程序二... 7
        3.3.1 程序设计[程序-3-2] 7
        3.3.2 Log信息[Log-3-2] 8
    3.4 结果分析... 8
4. 信号量(Semaphore)... 9
    4.1 问题的提出... 9
    4.2 验证程序一... 9
        4.2.1 程序设计[程序-4-1] 9
        4.2.2 Log信息[Log-4-1] 10
    4.3 验证程序二... 11
        4.3.1 程序设计[程序-4-2] 11
        4.3.2 Log信息[Log-4-2] 11
    4.4 结果分析... 12
5. 事件(Event)... 12
    5.1 问题描述... 12
    5.2 验证程序一... 12
        5.2.1 程序设计[程序-5-1] 12
        5.2.2 Log信息[Log-5-1] 12
        5.2.3结果分析... 13
    5.3 验证程序二... 13
        5.3.1 程序设计[程序-5-2] 13
        5.3.2 Log信息[Log-5-2] 14
        5.3.3 结果分析... 15
    5.4 事件(Event)机制小结... 15
6. 消息队列(MsgQueue P2P)... 15
    6.1 问题的提出... 15
    6.2 验证程序一... 15
        6.2.1 程序设计[程序-6-1] 15
        6.2.2 Log信息[Log-6-1] 17
    6.3 验证程序二... 18
        6.3.1 程序设计[程序-6-2] 18
        6.3.2 Log信息[Log-6-2] 19
    6.4 小节... 20
7. 总结... 20
参考资料以及进一步阅读... 20
关于作者... 20

1. 前言

看完《Windows® CE系统中的同步机制》之后,读者是不是还有很多问题,笔者也是这样,光从MSDN文档的描述,还有很多未知的和没弄明白的地方。笔者也没有什么WinCE的内幕资料,所以也只能根据问题用程序来分析验证。所以本文的组织方式是:提出问题——程序验证——结果分析——归纳总结。如果读者已经掌握问题分析的方法,只是关心同步问题的结果,可直接看各小节的提出的问题和总结部分以及最终的总结。不过笔者还是推荐您从头至尾阅读全文,并且最好对WinCE的同步机制有个大概的了解或者已经阅读完《Windows® CE系统中的同步机制》。

2. 临界区(CriticalSection)

2.1 问题的提出

问题描述:如果有多个线程都被挂起等待进入同一个临界区,当临界区内的线程离开临界区之后,WinCE选择哪个线程进入临界区,根据什么原则?

要分析解决这个问题,用两个程序来验证。

2.2 验证程序一

2.2.1 程序设计[程序-2-1]

一个进程的主线程创建一个临界区和3个子线程,并且这三个子线程的优先级相同。

三个子线程的执行体分别循环执行下列操作:

子线程Thread1

打印“Thread1 tries to EnterCriticalSection!”
执行MyCriticalSection()
打印“Thread1 Left the CriticalSection!”

子线程Thread2

打印“Thread2 tries to EnterCriticalSection!”
执行MyCriticalSection()
打印“Thread2 Left the CriticalSection!”

子线程Thread3

打印“Thread3 tries to EnterCriticalSection!”
执行MyCriticalSection()
打印“Thread3 Left the CriticalSection!”

MyCriticalSection()执行下列操作:

执行EnterCriticalSection (&hCriticalSection);
       打印“Thread is in CriticalSection speaking!”
       执行LeaveCriticalSection (&hCriticalSection);

2.2.2 Log信息[Log-2-1]

上述程序在WinCE 5.0 Platform Builder中运行,当输出稳定之后的打印信息如下。

为了明确起见,用下列区分要关注的信息:

红色加粗标识子线程1(Thread1)的输出;
       绿色加粗标识子线程2(Thread2)的输出;
       蓝色加粗标识子线程3(Thread3)的输出;

4294912246 PID:c3efda6e TID:83e184e2 Thread1 Left the CriticalSection!
4294912276 PID:c3efda6e TID:e3ca4fd2 Thread2 tries to EnterCriticalSection!
4294912276 PID:c3efda6e TID:e3cc 8c 3a Thread3 tries to EnterCriticalSection!
4294912276 PID:c3efda6e TID:83e184e2 Thread1 tries to EnterCriticalSection!
4294912276 PID:c3efda6e TID:e3ca4fd2 Thread is in CriticalSection speaking!
4294912276 PID:c3efda6e TID:e3ca4fd2 Thread2 Left the CriticalSection!
4294912296 PID:c3efda6e TID:e3ca4fd2 Thread2 tries to EnterCriticalSection!
4294912306 PID:c3efda6e TID:e3cc 8c 3a Thread is in CriticalSection speaking!
4294912306 PID:c3efda6e TID:e3cc 8c 3a Thread3 Left the CriticalSection!
4294912306 PID:c3efda6e TID:e3cc 8c 3a Thread3 tries to EnterCriticalSection!
4294912316 PID:c3efda6e TID:83e184e2 Thread is in CriticalSection speaking!
4294912316 PID:c3efda6e TID:83e184e2 Thread1 Left the CriticalSection!
4294912316 PID:c3efda6e TID:83e184e2 Thread1 tries to EnterCriticalSection!
4294912316 PID:c3efda6e TID:e3ca4fd2 Thread is in CriticalSection speaking!
4294912326 PID:c3efda6e TID:e3ca4fd2 Thread2 Left the CriticalSection!
4294912326 PID:c3efda6e TID:e3ca4fd2 Thread2 tries to EnterCriticalSection!
4294912326 PID:c3efda6e TID:e3cc 8c 3a Thread is in CriticalSection speaking!
4294912326 PID:c3efda6e TID:e3cc 8c 3a Thread3 Left the CriticalSection!
4294912336 PID:c3efda6e TID:e3cc 8c 3a Thread3 tries to EnterCriticalSection!
4294912336 PID:c3efda6e TID:83e184e2 Thread is in CriticalSection speaking!
4294912336 PID:c3efda6e TID:83e184e2 Thread1 Left the CriticalSection!
...

统计这样的打印信息2086条,分布情况如下:

Thread1696条;
Thread2695条;
Thread3695条。

2.3 验证程序二

2.3.1 程序设计[程序-2-2]

一个进程的主线程创建一个临界区和3个子线程,并且这三个子线程中Thread1和Thread2的优先级相同,并且都低于Thread3的优先级(优先级部分是与程序一唯一不同的地方)。

三个子线程的执行体分别循环执行下列操作:

子线程Thread1

打印“Thread1 tries to EnterCriticalSection!”
执行MyCriticalSection()
打印“Thread1 Left the CriticalSection!”

子线程Thread2

打印“Thread2 tries to EnterCriticalSection!”
执行MyCriticalSection()
打印“Thread2 Left the CriticalSection!”

子线程Thread3

打印“Thread3 tries to EnterCriticalSection!”
执行MyCriticalSection()
打印“Thread3 Left the CriticalSection!”

MyCriticalSection()执行下列操作:

执行EnterCriticalSection (&hCriticalSection);
       打印“Thread is in CriticalSection speaking!”
       执行LeaveCriticalSection (&hCriticalSection);

2.3.2 Log信息[Log-2-2]

上述程序在WinCE 5.0 Platform Builder中运行,当输出稳定之后的打印信息如下。

为了明确起见,用下列区分要关注的信息:

红色加粗标识子线程1(Thread1)的输出;
       绿色加粗标识子线程2(Thread2)的输出;
       蓝色加粗标识子线程3(Thread3)的输出;

4294817516 PID:43caa3da TID:3caa 6c 2 Thread1 tries to EnterCriticalSection!
4294817516 PID:43caa3da TID:3caa 6c 2 Thread is in CriticalSection speaking!
4294817516 PID:43caa3da TID:3caa 6c 2 Thread1 Left the CriticalSection!
4294817536 PID:43caa3da TID:e3caa692 Thread2 tries to EnterCriticalSection!
4294817556 PID:43caa3da TID:e3ce0d66 Thread3 tries to EnterCriticalSection!
4294817556 PID:43caa3da TID:e3ce0d66 Thread is in CriticalSection speaking!
4294817556 PID:43caa3da TID:e3ce0d66 Thread3 Left the CriticalSection!
4294817556 PID:43caa3da TID:e3ce0d66 Thread3 tries to EnterCriticalSection!
4294817556 PID:43caa3da TID:e3ce0d66 Thread is in CriticalSection speaking!
4294817566 PID:43caa3da TID:e3ce0d66 Thread3 Left the CriticalSection!
4294817566 PID:43caa3da TID:e3ce0d66 Thread3 tries to EnterCriticalSection!
4294817566 PID:43caa3da TID:e3ce0d66 Thread is in CriticalSection speaking!
4294817566 PID:43caa3da TID:e3ce0d66 Thread3 Left the CriticalSection!
4294817566 PID:43caa3da TID:e3ce0d66 Thread3 tries to EnterCriticalSection!
4294817566 PID:43caa3da TID:e3ce0d66 Thread is in CriticalSection speaking!
4294817566 PID:43caa3da TID:e3ce0d66 Thread3 Left the CriticalSection!
4294817566 PID:43caa3da TID:e3ce0d66 Thread3 tries to EnterCriticalSection!
4294817566 PID:43caa3da TID:e3ce0d66 Thread is in CriticalSection speaking!
4294817576 PID:43caa3da TID:e3ce0d66 Thread3 Left the CriticalSection!

统计这样的打印信息4220条,分布情况如下:

Thread113条;
Thread212条;
Thread34195条。

2.4 结果分析

从Log-2-1的现象,三个子线程打印信息数目相同,也就是说,当就绪的优先级相同的多个线程同时竞争进入同一个临界区的时候,WinCE是按照先来先服务的原则选择进入临界区的线程的。从Log-2-2的结果分析来看,当就绪的优先级不同的多个线程同时竞争进入同一个临界区的时候,WinCE是按照优先级高低选择高优先级的线程进入临界区的。

总结来说就是,当就绪的多个线程同时竞争进入同一个临界区的时候,WinCE总是选择优先级最高的或者优先级相同的情况下选择最先申请进入的线程进入临界区。所以在应用临界区的时候,要充分考虑线程优先级的设计,既要考虑优先级高的线程能优先进入临界区,又要防止高优先级的线程一直占据临界区而使较低优先级的线程难以进入,引起“饥饿(Starving)”现象。

3. 互斥体(Mutex)

3.1 问题的提出

问题一、验证一个线程拥有一个互斥体之后,再次申请这个互斥体,WinCE的处理。
问题二、如果有多个线程都被挂起等待进入同一个互斥体,当互斥体的拥有者释放该互斥体之后,WinCE选择哪个线程拥有它,根据什么原则?

要分析解决这两个问题,用两个程序来验证。

3.2 验证程序一

3.2.1 程序设计[程序-3-1]

一个进程的主线程

创建三个具有相同优先级的子线程;
创建一个Mutex,Mutex的是否初始拥有者设置为FALSE。

三个子线程的执行体分别循环执行下列操作:

子线程Thread1

打印“Thread1 is Waiting on the Mutex!”
执行WaitForSingleObject(hMutex, INFINITE);
打印“"Thread1 operating on mutex-protecting object.”
打印“Thread1 Waiting on the Mutex AGAIN!”
执行WaitForSingleObject(hMutex, INFINITE);
打印“Thread1 operating on mutex-protecting object AGAIN.”
执行ReleaseMutex(hMutex);
打印“Thread1 Released the Mutex, 1st!”
执行ReleaseMutex(hMutex);
打印“Thread1 Released the Mutex, 2nd!”

子线程Thread2

打印“Thread2 is Waiting on the Mutex!”
执行WaitForSingleObject(hMutex, INFINITE);
打印“Thread2 operating on mutex-protecting object.”
执行ReleaseMutex(hMutex);
打印“Thread2 Released the Mutex!/n”

子线程Thread3

打印“Thread2 is Waiting on the Mutex!”
执行WaitForSingleObject(hMutex, INFINITE);
打印“Thread2 operating on mutex-protecting object.”
执行ReleaseMutex(hMutex);
打印“Thread2 Released the Mutex!/n”

3.2.2 Log信息[Log-3-1]

上述程序在WinCE 5.0 Platform Builder中运行,当输出稳定之后的打印信息如下。

为了明确起见,用下列区分要关注的信息:

红色加粗标识子线程1(Thread1)的输出;
       绿色加粗标识子线程2(Thread2)的输出;
       蓝色加粗标识子线程3(Thread3)的输出;

TID:23ca7ea6 Thread3 Released the Mutex!
TID:23ca7ea6 Thread3 is Waiting on the Mutex!
TID:a3ca7122 Thread1 operating on mutex-protecting object.
TID:a3ca7122 Thread1 Waiting on the Mutex AGAIN!
TID:a3ca7122 Thread1 operating on mutex-protecting object AGAIN.
TID:a3ca7122 Thread1 Released the Mutex, 1st!
TID:a3ca7122 Thread1 Released the Mutex, 2st!
TID:a3ca7122 Thread1 is Waiting on the Mutex!
TID:43ca7fce Thread2 operating on mutex-protecting object.
TID:43ca7fce Thread2 Released the Mutex!
TID:43ca7fce Thread2 is Waiting on the Mutex!
TID:23ca7ea6 Thread3 operating on mutex-protecting object.
TID:23ca7ea6 Thread3 Released the Mutex!
TID:23ca7ea6 Thread3 is Waiting on the Mutex!
TID:a3ca7122 Thread1 operating on mutex-protecting object.
TID:a3ca7122 Thread1 Waiting on the Mutex AGAIN!
TID:a3ca7122 Thread1 operating on mutex-protecting object AGAIN.
TID:a3ca7122 Thread1 Released the Mutex, 1st!
TID:a3ca7122 Thread1 Released the Mutex, 2st!
TID:a3ca7122 Thread1 is Waiting on the Mutex!
TID:43ca7fce Thread2 operating on mutex-protecting object.
TID:43ca7fce Thread2 Released the Mutex!
...

统计这样的打印信息8005条,分布情况如下:

Thread14002条;
Thread22001条;
Thread32002条。

3.3 验证程序二

3.3.1 程序设计[程序-3-2]

一个进程的主线程

创建三个子线程Thread1、Thread2和Thread3,Thread2和Thread3具有相同优先级,并且它们的优先级高于Thread1的优先级;
创建一个Mutex,Mutex的是否初始拥有者设置为FALSE。

三个子线程的执行体分别循环执行下列操作:

子线程Thread1

打印“Thread1 is Waiting on the Mutex!”
执行WaitForSingleObject(hMutex, INFINITE);
打印“"Thread1 operating on mutex-protecting object.”
打印“Thread1 Waiting on the Mutex AGAIN!”
执行WaitForSingleObject(hMutex, INFINITE);
打印“Thread1 operating on mutex-protecting object AGAIN.”
执行ReleaseMutex(hMutex);
打印“Thread1 Released the Mutex, 1st!”
执行ReleaseMutex(hMutex);
打印“Thread1 Released the Mutex, 2nd!”

子线程Thread2

打印“Thread2 is Waiting on the Mutex!”
执行WaitForSingleObject(hMutex, INFINITE);
打印“Thread2 operating on mutex-protecting object.”
执行ReleaseMutex(hMutex);
打印“Thread2 Released the Mutex!/n”

子线程Thread3

打印“Thread2 is Waiting on the Mutex!”
执行WaitForSingleObject(hMutex, INFINITE);
打印“Thread2 operating on mutex-protecting object.”
执行ReleaseMutex(hMutex);
打印“Thread2 Released the Mutex!/n”

3.3.2 Log信息[Log-3-2]

上述程序在WinCE 5.0 Platform Builder中运行,当输出稳定之后的打印信息如下。

为了明确起见,用下列区分要关注的信息:

红色加粗标识子线程1(Thread1)的输出;
       绿色加粗标识子线程2(Thread2)的输出;
       蓝色加粗标识子线程3(Thread3)的输出;

TID:83ca 855a Thread1 Released the Mutex, 1st!
TID:83ca 855a Thread1 Released the Mutex, 2st!
TID:83ca 855a Thread1 is Waiting on the Mutex!
TID:83ca 855a Thread1 operating on mutex-protecting object.
TID:83ca 855a Thread1 Waiting on the Mutex AGAIN!
TID:23ccf56e Thread2 is Waiting on the Mutex!
TID:a3ca 891a Thread3 is Waiting on the Mutex!
TID:83ca 855a Thread1 operating on mutex-protecting object AGAIN.
TID:83ca 855a Thread1 Released the Mutex, 1st!
TID:23ccf56e Thread2 operating on mutex-protecting object.
TID:23ccf56e Thread2 Released the Mutex!
TID:23ccf56e Thread2 is Waiting on the Mutex!
TID:a3ca 891a Thread3 operating on mutex-protecting object.
TID:a3ca 891a Thread3 Released the Mutex!
TID:a3ca 891a Thread3 is Waiting on the Mutex!
TID:23ccf56e Thread2 operating on mutex-protecting object.
TID:23ccf56e Thread2 Released the Mutex!
TID:23ccf56e Thread2 is Waiting on the Mutex!
...

统计这样的打印信息2933条,分布情况如下:

Thread18条;
Thread21463条;
Thread31462条。

3.4 结果分析

从Log-3-1的现象来看,Thread1拥有一个互斥体并且在再未释放之前,别的线程无法拥有该互斥体;而Thread1再次申请该互斥体的拥有权的时候,可立即获得并不会被挂起;Thread1在把该互斥体释放两次之前,别的线程不会中途获得该互斥体的拥有权。Thread2和Thread3的各种参数都相同,它们打印的信息也一样的,也就是获得了同等竞争同一互斥体的机会。

从Log-3-2的现象来看,Thread1拥有一个互斥体并且在再未释放之前,别的线程无法拥有该互斥体;而Thread1再次申请该互斥体的拥有权的时候,可立即获得并不会被挂起;Thread1在把该互斥体释放两次之前,别的线程不会中途获得该互斥体的拥有权。这里有个特殊的地方,看Log-3-2斜体的地方,Thread1释放一次该互斥体的时候,Thread2就拥有了该互斥体了,其实这与上面的表述还是一致的,在这两个打印语句中间隐含着这样的信息,因为Thread1没有完全释放该互斥体的时候,Thread2不可能获得[这点在MSDN里面是有明确表述的]的,所以Thread2打印的那句话之前Thread1已经执行了第二次释放,只是相应释放之后的打印语句在释放语句之后,而Thread2的优先级高于Thread1,所以Thread2抢先打印了Thread2获得互斥体之后的那个操作的语句。Thread2和Thread3的各种参数都相同,它们打印的信息也一样的,也就是获得了同等竞争同一互斥体的机会,而Thread1的输出语句相比Thread2和Thread3的输出就少得可以忽略。

所以,验证了一个线程拥有一个互斥体之后,再次申请这个互斥体,可立即获得,而两次释放该互斥体之后,其它线程才可拥有。有多个线程等待同一个互斥体的时,WinCE选择优先级高的线程优先获得该互斥体,优先级相同时,按照先来先服务来选择最先申请的线程。

4. 信号量(Semaphore)

4.1 问题的提出

考虑下列两个资源分配的情况:

【资源分配情况一】两个线程申请同一类资源,这类资源总数为2,Thread1完成某个操作需要2个这样的资源,Thread2完成某个操作只需1个这样的资源。如果Thread1已经获得了其中一个资源,现在Thread1和Thread2一起竞争这个资源,这样的话,WinCE对Thread1有没有优待?

【资源分配情况二】两个线程申请同一类资源,这类资源总数为2,Thread1和Thread2完成某个操作都需要2个这样的资源。WinCE对防止死锁有没有处理?

用两个程序来看这两个资源分配的情况。

4.2 验证程序一

4.2.1 程序设计[程序-4-1]

一个进程的主线程

创建两个具有相同优先级的子线程Thread1和Thread2;
创建一个信号量,该信号量的最大值为2,初始值为0。等到两个子线程都运行起来之后,用ReleaseSemaphore()释放2个资源。

两个子线程的执行体分别循环执行下列操作:

子线程Thread1

打印“Thread1 is Waiting on the 1st Semaphore!”
执行WaitForSingleObject(hSema, INFINITE);
打印“Thread1 GOT the 1st Semaphore!”
打印“Thread1 is Waiting on the 2nd Semaphore!”
执行WaitForSingleObject(hSema, INFINITE);
打印“Thread1 GOT the 2nd Semaphore!”
打印“Thread1 operating on the resources.”
执行ReleaseSemaphore(hSema, 2, &prevCount);
打印“Thread1 Released TWO Semaphores!”

子线程Thread2

打印“Thread2 is Waiting on the Semaphore!”
执行WaitForSingleObject(hSema, INFINITE);
打印“Thread2 operating on the resource.”
执行ReleaseSemaphore(hSema, 1, &prevCount);
打印“Thread2 Released ONE Semaphore!”

4.2.2 Log信息[Log-4-1]

上述程序在WinCE 5.0 Platform Builder中运行,当输出稳定之后的打印信息如下。

为了明确起见,用下列区分要关注的信息:

红色加粗标识子线程1(Thread1)的输出;
       绿色加粗标识子线程2(Thread2)的输出;

PID:a3cac0da TID:c3cdc4ca Thread1 is Waiting on the 1st Semaphore!
PID:a3cac0da TID:23cacfbe Thread2 is Waiting on the Semaphore!
PID:a3cac0da TID:c3cdc4ca Thread1 GOT the 1st Semaphore!
PID:a3cac0da TID:c3cdc4ca Thread1 is Waiting on the 2nd Semaphore!
PID:a3cac0da TID:23cacfbe Thread2 operating on the resource.
PID:a3cac0da TID:23cacfbe Thread2 Released ONE Semaphore!
PID:a3cac0da TID:23cacfbe Thread2 is Waiting on the Semaphore!
PID:a3cac0da TID:c3cdc4ca Thread1 GOT the 2nd Semaphore!
PID:a3cac0da TID:c3cdc4ca Thread1 operating on the resources.
PID:a3cac0da TID:c3cdc4ca Thread1 Released TWO Semaphores!
PID:a3cac0da TID:c3cdc4ca Thread1 is Waiting on the 1st Semaphore!
PID:a3cac0da TID:c3cdc4ca Thread1 GOT the 1st Semaphore!
PID:a3cac0da TID:c3cdc4ca Thread1 is Waiting on the 2nd Semaphore!
PID:a3cac0da TID:23cacfbe Thread2 operating on the resource.
PID:a3cac0da TID:23cacfbe Thread2 Released ONE Semaphore!
PID:a3cac0da TID:23cacfbe Thread2 is Waiting on the Semaphore!
PID:a3cac0da TID:c3cdc4ca Thread1 GOT the 2nd Semaphore!
PID:a3cac0da TID:c3cdc4ca Thread1 operating on the resources.
PID:a3cac0da TID:c3cdc4ca Thread1 Released TWO Semaphores!
PID:a3cac0da TID:c3cdc4ca Thread1 is Waiting on the 1st Semaphore!
PID:a3cac0da TID:c3cdc4ca Thread1 GOT the 1st Semaphore!
PID:a3cac0da TID:c3cdc4ca Thread1 is Waiting on the 2nd Semaphore!
PID:a3cac0da TID:23cacfbe Thread2 operating on the resource.
PID:a3cac0da TID:23cacfbe Thread2 Released ONE Semaphore!
PID:a3cac0da TID:23cacfbe Thread2 is Waiting on the Semaphore!
PID:a3cac0da TID:c3cdc4ca Thread1 GOT the 2nd Semaphore!
PID:a3cac0da TID:c3cdc4ca Thread1 operating on the resources.
PID:a3cac0da TID:c3cdc4ca Thread1 Released TWO Semaphores!
PID:a3cac0da TID:c3cdc4ca Thread1 is Waiting on the 1st Semaphore!
PID:a3cac0da TID:c3cdc4ca Thread1 GOT the 1st Semaphore!
...

4.3 验证程序二

4.3.1 程序设计[程序-4-2]

一个进程的主线程

创建两个子线程Thread1和Thread2,且具有相同优先级;
创建一个信号量,该信号量的最大值为2,初始值为0。等到两个子线程都运行起来之后,用ReleaseSemaphore()释放出2个资源。

两个子线程的执行体分别循环执行下列操作:

子线程Thread1

打印“Thread1 is Waiting on the 1st Semaphore!”
执行WaitForSingleObject(hSema, INFINITE);
打印“Thread1 GOT the 1st Semaphore!”
打印“Thread1 is Waiting on the 2nd Semaphore!”
执行WaitForSingleObject(hSema, INFINITE);
打印“Thread1 GOT the 2nd Semaphore!”
打印“Thread1 operating on the resources.”
执行ReleaseSemaphore(hSema, 2, &prevCount);
打印“Thread1 Released TWO Semaphores!”

子线程Thread2

打印“Thread2 is Waiting on the 1st Semaphore!”
执行WaitForSingleObject(hSema, INFINITE);
打印“Thread2 GOT the 1st Semaphore!”
打印“Thread2 is Waiting on the 2nd Semaphore!”
执行WaitForSingleObject(hSema, INFINITE);
打印“Thread2 GOT the 2nd Semaphore!”
打印“Thread2 operating on the resources.”
执行ReleaseSemaphore(hSema, 2, &prevCount);
打印“Thread2 Released TWO Semaphores!”

4.3.2 Log信息[Log-4-2]

上述程序在WinCE 5.0 Platform Builder中运行,当输出稳定之后的打印信息如下。

为了明确起见,用下列区分要关注的信息:

红色加粗标识子线程1(Thread1)的输出;
       绿色加粗标识子线程2(Thread2)的输出;

PID:c3cad9b2 TID:23ce 599a Thread1 is Waiting on the 1st Semaphore!
PID:c3cad9b2 TID:23cada12 Thread2 is Waiting on the 1st Semaphore!
PID:c3cad9b2 TID:23ce 599a Thread1 GOT the 1st Semaphore!
PID:c3cad9b2 TID:23ce 599a Thread1 is Waiting on the 2nd Semaphore!
PID:c3cad9b2 TID:23cada12 Thread2 GOT the 1st Semaphore!
PID:c3cad9b2 TID:23cada12 Thread2 is Waiting on the 2nd Semaphore!

Dead-Locked!

4.4 结果分析

从Log-4-1的现象来看,不管Thread1的第一次资源申请,Thread1的第二次资源申请,还是Thread2的资源申请,WinCE都是同等对待的,对于这三个申请是按照先来先服务的分配策略来分配的。从Log-4-2的现象看,Thread1和Thread2各自拥有了一个此类资源并再申请另外一个,导致死锁(Dead-lock)。

WinCE对于竞争分配信号量的情况没有做特殊的处理,所以,设计程序的时候,要自己来保证死锁(Dead-lock)的预防和处理。

5. 事件(Event)

5.1 问题描述

[验证]自动复位是等待该事件的线程被从等待状态恢复之后,系统自动把该事件的状态复位为未触发的。

[问题]多个线程等待同一事件,是广播通知到每个线程还是通知其中的一个。

5.2 验证程序一

5.2.1 程序设计[程序-5-1]

验证程序的主线程,创建了一个事件对象和三个子线程,并随后启动了这三个子线程。创建事件的代码如下:

hEvent = CreateEvent(NULL,
       FALSE,       // blManual
       FALSE,       // blInitialState
       NULL);

子线程Thread1和Thread2具有相同的优先级,且它们的优先级高于子线程Thread3。三个子线程的执行体分别循环执行下列操作:

子线程Thread1

打印“Thread1: Waiting on the Event.”;
执行WaitForSingleObject(hEvent, INFINITE);
打印“Thread1: BE INFORMED by the Event.”。

子线程Thread2

打印“Thread2: Waiting on the Event.”;
执行WaitForSingleObject(hEvent, INFINITE);
打印“Thread2: BE INFORMED by the Event.”。

子线程Thread3

执行“SetEvent(hEvent)
打印“Thread3: SET the Event.”;

5.2.2 Log信息[Log-5-1]

上述程序在WinCE 5.0 Platform Builder中运行,当输出稳定之后的打印信息如下。

为了明确起见,用下列区分要关注的信息:

红色加粗标识子线程1(Thread1)的输出;
       绿色加粗标识子线程2(Thread2)的输出;
       蓝色加粗标识子线程3(Thread3)的输出;

PID:e3caad82 TID: 83c 9f 34a Thread2: Waiting on the Event.
PID:e3caad82 TID:83dd1fd2 Thread1: Waiting on the Event.
PID:e3caad82 TID:a3cfb872 Thread3: SET the Event.
PID:e3caad82 TID: 83c 9f 34a Thread2: BE INFORMED by the Event.
PID:e3caad82 TID: 83c 9f 34a Thread2: Waiting on the Event.
PID:e3caad82 TID:a3cfb872 Thread3: SET the Event.
PID:e3caad82 TID:83dd1fd2 Thread1: BE INFORMED by the Event.
PID:e3caad82 TID:83dd1fd2 Thread1: Waiting on the Event.
PID:e3caad82 TID:a3cfb872 Thread3: SET the Event.
PID:e3caad82 TID: 83c 9f 34a Thread2: BE INFORMED by the Event.
PID:e3caad82 TID: 83c 9f 34a Thread2: Waiting on the Event.
PID:e3caad82 TID:a3cfb872 Thread3: SET the Event.
PID:e3caad82 TID:83dd1fd2 Thread1: BE INFORMED by the Event.
PID:e3caad82 TID:83dd1fd2 Thread1: Waiting on the Event.
PID:e3caad82 TID:a3cfb872 Thread3: SET the Event.
PID:e3caad82 TID: 83c 9f 34a Thread2: BE INFORMED by the Event.
PID:e3caad82 TID: 83c 9f 34a Thread2: Waiting on the Event.
PID:e3caad82 TID:a3cfb872 Thread3: SET the Event.
PID:e3caad82 TID:83dd1fd2 Thread1: BE INFORMED by the Event.
PID:e3caad82 TID:83dd1fd2 Thread1: Waiting on the Event.
...

统计4241条这样的打印结果,分布情况如下:

Thread11413条;
Thread21415条;
Thread31413条。

5.2.3 结果分析

从Log-5-1的现象来看,Thread1和Thread2情况一样。Thread1和Thread2都在等待Thread3设置的事件,也就是Thread3执行两次循环,Thread1和Thread2才能执行一次循环,因为Thread3的循环体内只有一条打印语句,而Thread1和Thread2的循环体内各自都有两条打印语句,所以这里的Thread1、Thread2和Thread3具有相同数目的打印信息。

[Solution to Q1]同时也说明,在Thread1和Thread2从执行WaitForSingleObject(hEvent)的等待状态返回的时候,相应的事件是自动被复位的。[反证法]如果未被复位,Thread1和Thread2的优先级是高于Thread3的,如果Thread1和Thread2不被WaitForSingleObject()挂起的话,Thread3根本就没有机会被调度运行,至少Thread3打印出的语句不会那么多。

[Solution to Q2]对于自动复位的事件,事件被触发之后的通知只是通知等待线程中的一个。

5.3 验证程序二

5.3.1 程序设计[程序-5-2]

(红色显示与[程序-5-1]的区别)

验证程序的主线程,创建了一个事件对象和三个子线程,并随后启动了这三个子线程。创建事件的代码如下:

hEvent = CreateEvent(NULL,
       TRUE,         // blManual
       FALSE,       // blInitialState
       NULL);

子线程Thread1和Thread2具有相同的优先级,且它们的优先级高于子线程Thread3,三个子线程的执行体分别循环执行下列操作:

子线程Thread1

打印“Thread1: Waiting on the Event.”;
执行WaitForSingleObject(hEvent, INFINITE);
打印“Thread1: BE INFORMED by the Event.”;
执行ResetEvent(hEvent)。

子线程Thread2

打印“Thread2: Waiting on the Event.”;
执行WaitForSingleObject(hEvent, INFINITE);
打印“Thread2: BE INFORMED by the Event.”;
执行ResetEvent(hEvent)。

子线程Thread3

执行“SetEvent(hEvent)
打印“Thread3: SET the Event.”;

5.3.2 Log信息[Log-5-2]

上述程序在WinCE 5.0 Platform Builder中运行,当输出稳定之后的打印信息如下。

为了明确起见,用下列区分要关注的信息:

红色加粗标识子线程1(Thread1)的输出;
       绿色加粗标识子线程2(Thread2)的输出;
       蓝色加粗标识子线程3(Thread3)的输出;

PID:a3cc 637a TID:83dd4fa6 Thread1: Waiting on the Event.
PID:a3cc 637a TID:83caeb 2a Thread2: Waiting on the Event.
PID:a3cc 637a TID:83dd4fa6 Thread1: BE INFORMED by the Event.
PID:a3cc 637a TID:83dd4fa6 Thread1: Waiting on the Event.
PID:a3cc 637a TID:83caeb 2a Thread2: BE INFORMED by the Event.
PID:a3cc 637a TID:83caeb 2a Thread2: Waiting on the Event.
PID:a3cc 637a TID:43cfaafe Thread3: SET the Event.
PID:a3cc 637a TID:83dd4fa6 Thread1: BE INFORMED by the Event.
PID:a3cc 637a TID:83dd4fa6 Thread1: Waiting on the Event.
PID:a3cc 637a TID:83caeb 2a Thread2: BE INFORMED by the Event.
PID:a3cc 637a TID:83caeb 2a Thread2: Waiting on the Event.
PID:a3cc 637a TID:43cfaafe Thread3: SET the Event.
PID:a3cc 637a TID:83dd4fa6 Thread1: BE INFORMED by the Event.
PID:a3cc 637a TID:83dd4fa6 Thread1: Waiting on the Event.
PID:a3cc 637a TID:83caeb 2a Thread2: BE INFORMED by the Event.
PID:a3cc 637a TID:83caeb 2a Thread2: Waiting on the Event.
PID:a3cc 637a TID:43cfaafe Thread3: SET the Event.

统计7296条这样的打印结果,分布情况如下:

Thread12919条;
Thread22919条;
Thread31458条。

5.3.3 结果分析

[Log-5-2]的现象,Thread1与程序一相比,这个程序的Thread3的一个SetEvent()设置广播设置了Thread1和Thread2等待的事件,所以Thread3的打印语句刚好是Thread1的一半。

这也就说明了第二个问题的一种情况,亦即,对于手动复位的事件,事件被触发之后的通知是一个广播通知,通知所有在等待的线程。

5.4 事件(Event)机制小结

从这两个程序的验证来看,自动复位是等待该事件的线程被从等待状态恢复之后,系统自动把该事件的状态复位为未触发的。

对于自动复位的事件,事件被触发之后的通知只是通知等待线程中的一个;而对于手动复位的事件,事件被触发之后的通知是一个广播通知,通知所有在等待的线程。

6. 消息队列(MsgQueue P2P)

6.1 问题的提出

[问题]一个消息队列的两端有多个读者、多个写者,WinCE怎么处理,会是哪个读者和写者能获得读和写的机会?

6.2 验证程序一

6.2.1 程序设计[程序-6-1]

程序的主线程,创建了一个消息队列和四个子线程,并随后启动了这四个子线程。

创建消息队列并连通这个消息队列的代码片段如下:

// Reader Options
    g_Options_r.dwSize = 20;
    g_Options_r.dwFlags = MSGQUEUE_NOPRECOMMIT;
    g_Options_r.dwMaxMessages = 4;
    g_Options_r.cbMaxMessage = 8;
    g_Options_r.bReadAccess = TRUE;

// Writer Options
    g_Options_w.dwSize = 20;
    g_Options_w.dwFlags = MSGQUEUE_NOPRECOMMIT;
    g_Options_w.dwMaxMessages = 4;
    g_Options_w.cbMaxMessage = 8;
    g_Options_w.bReadAccess = FALSE;

// Create the MsgQueue
    g_hMsgQ = CreateMsgQueue(TEXT("Reader/Writer MsgQueue"), &g_Options_r);

子线程Thread1和Thread2具有相同的优先级,都以读访问方式打开主线程创建的消息队列;子线程Thread3和Thread4具有相同的优先级,都以写访问方式打开主线程创建的消息队列。

子线程Thread1的执行体完成下列初始化工作:

char buff[9];
    HANDLE hMsgQ = OpenMsgQueue(hMainThread, g_hMsgQ, &g_Options_r);

DWORD error = GetLastError();
    if (error == ERROR_ALREADY_EXISTS)
    {
        printf("[ReaderThread1]: Connected to the MsgQueue[Reader]/n");
    }
    else if (error == ERROR_SUCCESS)
    {
        printf("[ReaderThread1]: Opened a MsgQueue[Reader]/n");
    }

然后,Thread1循环执行下列操作:

DWORD byteCount = 0;
    DWORD flag = 0;

WaitForSingleObject(hMsgQ, INFINITE);
    ReadMsgQueue(hMsgQ, &buff, 8, &byteCount, INFINITE, &flag);
    if (flag == MSGQUEUE_MSGALERT)
    {
        printf("[ReaderThread1]: Received an Alert MSG/n");
    }
    else
    {
        buff[8] = '/0';
        printf("[ReaderThread1]: Received %d BYTES MSG: %s/n", byteCount, buff);
    }

子线程Thread2的执行体同Thread1的,不同之处仅仅是把打印信息部分由ReaderThread1改为ReaderThread2。

子线程Thread3的执行体首先完成下列初始化工作:

char buff[9];
    HANDLE hMsgQ = OpenMsgQueue(hMainThread, g_hMsgQ, &g_Options_w);

DWORD error = GetLastError();
    if (error == ERROR_ALREADY_EXISTS)
    {
        printf("[WriterThread1]: Connected to the MsgQueue[Writer]/n");
    }
    else if (error == ERROR_SUCCESS)
    {
        printf("[WriterThread1]: Opened a MsgQueue[Writer]/n");
    }

然后,Thread3循环执行下列操作:

DWORD flag = 0;
    static DWORD counter = 1;
   
    sprintf(buff, "W1: %4d", counter);
    if (counter%10 == 0)
    {
        flag = MSGQUEUE_MSGALERT;
        printf("[WriterThread1]: To Send an Alert MSG/n");
    }
    else
    {
        printf("[WriterThread1]: To Send 8 BYTES MSG: %s/n", buff);
    }

WaitForSingleObject(hMsgQ, INFINITE);
    WriteMsgQueue(hMsgQ, &buff[0], 8, INFINITE, flag);
   
    counter++;

子线程Thread4的执行体同Thread3的,不同之处仅仅是把打印信息部分由WriterThread1改为WriterThread2,并把语句“sprintf(buff, "W1: %4d", counter);改为sprintf(buff, "W4: %4d", counter);

6.2.2 Log信息[Log-6-1]

为了明确起见,用下列区分要关注的信息:

红色加粗标识子线程1(Thread1)的输出;
       绿色加粗标识子线程2(Thread2)的输出;
       蓝色加粗标识子线程3(Thread3)的输出;
       灰色加粗标识子线程4(Thread4)的输出。

上述程序在WinCE 5.0 Platform Builder中运行,初始的输出如下:

TID:63e 26ac 2 [ReaderThread1]: Opened a MsgQueue[Reader]
TID:c3ca8dae [ReaderThread2]: Opened a MsgQueue[Reader]
TID:3cd5722 [WriterThread1]: Opened a MsgQueue[Writer]
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1:    1
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1:    2
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1:    3
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1:    4
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1:    5
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1:    1
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1:    2
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1:    3
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1:    4
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1:    6
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1:    7
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1:    8
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1:    9
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1:    5
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1:    6
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1:    7
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1:    8
...

上面没有Thread4的信息,那是线程调度的问题,在打印的第3211行看到Thread4刚刚被调度运行,以写的方式打开消息队列:

TID: 83c 97fba [WriterThread2]: Opened a MsgQueue[Writer]

输出稳定之后的打印信息如下:

TID: 83c 97fba [WriterThread2]: To Send 8 BYTES MSG: W2:    1
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1: 1603
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1: 1602
TID: 83c 97fba [WriterThread2]: To Send 8 BYTES MSG: W2:    2
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1: 1604
TID:c3ca8dae [ReaderThread2]: Received 8 BYTES MSG: W2:    1
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1: 1603
TID: 83c 97fba [WriterThread2]: To Send 8 BYTES MSG: W2:    3
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1: 1605
TID:c3ca8dae [ReaderThread2]: Received 8 BYTES MSG: W2:    2
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1: 1604
TID: 83c 97fba [WriterThread2]: To Send 8 BYTES MSG: W2:    4
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1: 1606
TID:c3ca8dae [ReaderThread2]: Received 8 BYTES MSG: W2:    3
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1: 1605
TID: 83c 97fba [WriterThread2]: To Send 8 BYTES MSG: W2:    5
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1: 1607
TID:c3ca8dae [ReaderThread2]: Received 8 BYTES MSG: W2:    4
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1: 1606
TID: 83c 97fba [WriterThread2]: To Send 8 BYTES MSG: W2:    6
TID:3cd5722 [WriterThread1]: To Send 8 BYTES MSG: W1: 1608
TID:c3ca8dae [ReaderThread2]: Received 8 BYTES MSG: W2:    5
TID:63e 26ac 2 [ReaderThread1]: Received 8 BYTES MSG: W1: 1607
...

6.3 验证程序二

6.3.1 程序设计[程序-6-2]

程序除了下列更改之外,其它同[程序-6-1]。

子线程Thread1的优先级低于子线程Thread1;子线程Thread3的优先级低于子线程Thread4。

6.3.2 Log信息[Log-6-2]

为了明确起见,用下列区分要关注的信息:

红色加粗标识子线程1(Thread1)的输出;
       绿色加粗标识子线程2(Thread2)的输出;
       蓝色加粗标识子线程3(Thread3)的输出;
       灰色加粗标识子线程4(Thread4)的输出。

上述程序在WinCE 5.0 Platform Builder中运行,初始的输出如下:

PID:83e 00f 96 TID:63e 606f 2 [ReaderThread1]: Opened a MsgQueue[Reader]
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Opened a MsgQueue[Reader]
PID:83e 00f 96 TID:c3ca3542 [WriterThread1]: Opened a MsgQueue[Writer]
PID:83e 00f 96 TID:c3ca3542 [WriterThread1]: To Send 8 BYTES MSG: W1:    1
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Received 8 BYTES MSG: W1:    1
PID:83e 00f 96 TID:c3ca3542 [WriterThread1]: To Send 8 BYTES MSG: W1:    2
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Received 8 BYTES MSG: W1:    2
PID:83e 00f 96 TID:c3ca3542 [WriterThread1]: To Send 8 BYTES MSG: W1:    3
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Received 8 BYTES MSG: W1:    3
PID:83e 00f 96 TID:c3ca3542 [WriterThread1]: To Send 8 BYTES MSG: W1:    4
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Received 8 BYTES MSG: W1:    4
...

Thread4未被调度之前,一直只有Thread3[WriterThread1]写数据到消息队列,Thread2[ReaderThread2]从消息队列中读数据。

输出稳定之后的打印信息如下:

PID:83e 00f 96 TID:a3ca3572 [WriterThread2]: To Send 8 BYTES MSG: W2:    1
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Received 8 BYTES MSG: W1: 2065
PID:83e 00f 96 TID:a3ca3572 [WriterThread2]: To Send 8 BYTES MSG: W2:    2
PID:83e 00f 96 TID:a3ca3572 [WriterThread2]: To Send 8 BYTES MSG: W2:    3
PID:83e 00f 96 TID:a3ca3572 [WriterThread2]: To Send 8 BYTES MSG: W2:    4
PID:83e 00f 96 TID:a3ca3572 [WriterThread2]: To Send 8 BYTES MSG: W2:    5
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Received 8 BYTES MSG: W2:    1
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Received 8 BYTES MSG: W2:    2
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Received 8 BYTES MSG: W2:    3
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Received 8 BYTES MSG: W2:    4
PID:83e 00f 96 TID:a3ca3572 [WriterThread2]: To Send 8 BYTES MSG: W2:    6
PID:83e 00f 96 TID:a3ca3572 [WriterThread2]: To Send 8 BYTES MSG: W2:    7
PID:83e 00f 96 TID:a3ca3572 [WriterThread2]: To Send 8 BYTES MSG: W2:    8
PID:83e 00f 96 TID:a3ca3572 [WriterThread2]: To Send 8 BYTES MSG: W2:    9
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Received 8 BYTES MSG: W2:    5
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Received 8 BYTES MSG: W2:    6
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Received 8 BYTES MSG: W2:    7
PID:83e 00f 96 TID:e3de 6f 02 [ReaderThread2]: Received 8 BYTES MSG: W2:    8
PID:83e 00f 96 TID:a3ca3572 [WriterThread2]: To Send an Alert MSG
PID:83e 00f 96 TID:a3ca3572 [WriterThread2]: To Send 8 BYTES MSG: W2:   11
...

一直只有Thread4[WriterThread2]写数据到消息队列,Thread2[ReaderThread2]从消息队列中读数据。

6.4 小节

从[Log-6-1]稳定之后的信息来看,读线程Thread1Thread2交叉从消息队列中读取数据,写线程Thread3Thread4交叉写数据到消息队列中。从[Log-6-2]稳定之后的信息来看,只有优先级较高的读线程Thread2从消息队列中读取数据,优先级较高的写线程Thread4写数据到消息队列中。

WinCE选择从消息队列读数据或写数据到消息队列的哪个线程获得读/写权限,是根据优先级高低选择的,优先级相同的情况下,选择最先申请者。

采用这种同步方式设计运行实体之间的通信时,WinCE不会保证某个进/线程能按顺序读/写的消息队列中的数据(有可能被别的进/线程读/写,并从消息队列中移除),需要开发者自己设计这种通信双方控制,这就需要用另外的同步机制。

7. 总结

WinCE提供了基本的进/线程之间的同步方式,当多个进/线程竞争某个同步对象的时候,WinCE是根据参与竞争的线程的优先级高低来选择的,高优先级优先获得所竞争的对象,相同优先级情况下,一般是先申请者获得所竞争的对象。所以在使用它们的时候,要充分考虑各种因素,在保证高优先级线程优先获得竞争对象的前提下,也要考虑低优先级线程的饥饿现象。同时也要考虑一个线程已经持有一个同步对象,再申请其它同步对象,而引起死锁的问题。

参考资料以及进一步阅读

1) MSDN
2) UML Reference Manual, 2nd Edition
3) Abraham Silberschatz, Peter Baer Galvin, Greg Gagne. Operating System Concepts, 6th Edition. John Wiley & Sons, Inc/高等教育出版社影印, 2002.5
4) 孙钟秀,费翔林,骆斌,谢立. 操作系统教程,第三版. 高等教育出版社,2003.8
5) David R. Butenhof/于磊,曾刚. Programming with POSIX Threads. Addison Wesley/中国电力出版社, 2003

关于作者

田海立,硕士,国家系统分析师,中国系统分析员协会顾问团专业顾问。您可以通过 haili.tian@csai.cn 或 tianhaili@nju.org.cn 与他联系,到 http://blog.csdn.net/thl789/ 看他最新的文章。

版权声明:

本文为作者原创,版权归作者所有。
为了学习和研究,可转载本文,但必须与原文的内容和格式保持一致,并给出原文的链接!http://blog.csdn.net/thl789/archive/2006/01/18/583315.aspx

关于 Windows CE 系统中同步机制的思考相关推荐

  1. Windows® CE 系统中的同步机制

    看到篇好文章,呵呵,独乐乐,不如众乐乐 本文转自http://blog.csdn.net/thl789/archive/2006/01/17/582246.aspx ,转载请注明出处 摘要 ... 1 ...

  2. windows系统多线程同步机制原理总结

    windows系统多线程同步机制原理总结 同步问题是开发过程中遇到的重要问题之一.同步是要保证在并发执行的环境中各个控制流可以有序地执行,包括对于资源的共享或互斥访问,以及代码功能的逻辑顺序. 为了保 ...

  3. Microsoft Windows CE .NET 中的中断体系结构

    概述 通过 Microsoft Windows CE .NET,Microsoft 已经升级了 Windows CE 的中断体系结构.该操作系统 (OS) 所具有的处理共享中断的能力极大地扩展了 Wi ...

  4. 在Windows10系统中同步Internet 时间

    在Windows10系统中同步Internet 时间: 进入控制面板,找到并左键双击[日期和时间]: 6 在打开的日期和时间窗口中,我们点击:Internet 时间: 7 在c选项卡下,我们点击:更改 ...

  5. Windows XP系统中如何屏蔽 Ctrl+Alt+Del、Alt+Tab以及Ctrl+Esc键序列

    Windows XP系统中如何屏蔽 Ctrl+Alt+Del.Alt+Tab以及Ctrl+Esc键序列 编译/northtibet 关键字:Ctrl+Alt+Del,Alt+Tab,Ctrl+Esc, ...

  6. 入手一个windows ce系统的可以打电话的HPC,测试在上面发表博客

    入手一个windows ce系统的可以打电话的HPC,测试在上面发表博客,浏览器效果还不错,和PC上效果接近,哈哈!

  7. 利用Windows 2003系统中实现两个网段的路由

    利用Windows 2003系统中实现两个网段的路由 当一个局域网中存在两个以上网段时,分属于不同网段内的主机彼此互不可见.为了解决这个问 题,就必须在不同的网段之间设置路由器.如果花费上万元资金购买 ...

  8. Windows XP系统中实用的命令及操作技巧

    Windows XP系统中实用的命令及操作技巧 转贴自:电脑报 想在命令提示符窗口中输入重复命令时,只须按F7键,就会出现图形界面,然后选择你想输入的命令即可. 一"符"安天下利用 ...

  9. Windows 7系统中的彩蛋“God Mode”

    估计这个彩蛋是windows编程开发人员给自己留的快捷功能. Windows 7系统中隐藏了一个秘密的"God Mode",字面上译为"上帝模式". God M ...

最新文章

  1. 突破电信3G宽带对网页浏览的上网限制
  2. AI检测贫血不看血,竟是看眼睛
  3. Java中Byte类型数据在运算中的问题
  4. Docker Swarm mode与滚动升级
  5. DHCP服务_学习笔记
  6. 网络基础:分享几个路由器设置小技巧,总有用得到的一天!
  7. CRTMPServer 在CentOS 64-bit下的编译(转)
  8. 拆分-洛谷P2745 [USACO5.3]窗体面积Window Area
  9. spark 不同模式用途_Spark 的四种模式
  10. HTML+CSS+JS实现 ❤️canvas酷炫表白爱心动画❤️
  11. 直连数据库实时更新数据,可视化报表这么做简直牛
  12. thinkphp连接远程数据库慢_干货分享—Niushop数据库配置
  13. 微信小程序生成海报分享:canvas绘制文字溢出如何换行
  14. linux安装svn服务器的两种方式(转载)
  15. php判断电话号码是否为空号,如何批量检测手机号码是否为空号、无效号码?
  16. F2FS源码分析-1.1 [F2FS 元数据布局部分] F2FS文件系统的总体结构
  17. 一些堪称神器却少为人知的网站或软件(整理自知乎)
  18. xxampp 配置php_MAC下使用XMAPP配置php环境
  19. cass等距离等分线段的命令键_CAD等分线段指令是什么?
  20. Alien Skin Exposure X5 Bundle Mac(PS/LR照片胶片滤镜插件)

热门文章

  1. 五、系统架构 - 高性能架构设计及性能优化
  2. python语音建模_该系统实现了基于深度框架的语音识别中的声学模型和语言模型建模...
  3. JSP页面格式化:金额、时间
  4. [秀] MarsBook 手机书吧
  5. 一文详解激光雷达原理之光学原理
  6. 按照RFC3984协议实现H264视频RTP打包(附源代码)
  7. Cognex Designer中的数据显示-----你不知道的BUG
  8. CCTV:未知木马防不胜防 主动防御是防毒软件发展方向
  9. WIN7设置wifi热点的方法
  10. 红米Note8首发,6400万+联发科G90T,能否再续Note7的辉煌