user-0 提问: 优先级逆转

回复:
user: user-1
content: 引用 12 楼 DelphiGuy 的回复:在循环中调用SwitchToThread()释放时间片的代价有点大，这意味着当前线程在当前处理器上的线程队列中要重新排队，（正常情况下）要执行一圈之后才能再次得到执行，如果活跃线程比较多、当前线程的优先级比较低，那可能要挂起比较长的时间，比如一两秒甚至更长。还是用Sleep/SleepEx释放一点时间片好一些，这只是暂时挂起当前线程一段时间，之后该线程会得到优先执行。  低优先级的饥饿线程， 达到3-4秒钟时候，系统会强制将其优先级提到15. 给2个cpu时间片。  最后回到该线程的基本优先级的。  也就所， 根本不存在 作者的说法，“This can happen if the primary thread has a higher priority than the thread executing the RecalcFunc function. In this case, the system never assigns any time slices to the RecalcFunc thread, which never executes the statement that sets g_fFinishedCalculation to TRUE.”  这段文字是有问题的。   当然，只是以下的做法，效率低一些而已。  volatile BOOL g_fFinishedCalculation = FALSE 主线程 { 创建子线程 while(!g_fFinishedCalculation);  }  子线程代码：  { g_fFinishedCalculation = TRUE; }  效率低， 不代表子线程没有机会执行。 在单核cpu上也是有执行机会的。  无论多核，单核。都有机会。  作者的说法不对

user: user-2
content: 在循环中调用SwitchToThread()释放时间片的代价有点大，这意味着当前线程在当前处理器上的线程队列中要重新排队，（正常情况下）要执行一圈之后才能再次得到执行，如果活跃线程比较多、当前线程的优先级比较低，那可能要挂起比较长的时间，比如一两秒甚至更长。还是用Sleep/SleepEx释放一点时间片好一些，这只是暂时挂起当前线程一段时间，之后该线程会得到优先执行。

user: user-0
content: 引用 10 楼 DelphiGuy 的回复:我觉得作者说的有问题，高优先级线程占用CPU时间多（比如一直轮询某个状态）并不代表低优先级线程得不到执行，抢先式多任务调度的处理方式是根据优先级和建立顺序进行排队，高优先级线程执行过一遍之后才轮到优先级低一档的线程执行。 当然作者说你不要一直轮询、要在循环中经常释放一点时间片，这肯定是好的编程习惯，可以降低CPU占用率、加快任务流转的速度、提高GUI的响应速度等等。 另外，7楼的代码是不会造成死循环的，只是因为他的代码子线程优先级低、循环时间长而已，把WORK_LOOP减小一下就可以看出来。比如这样（注意略加修改是为了便于使用非ms编译器通过，不影响代码执行效果）：  #include <stdio.h> #include <windows.h>   #pragma optimize("", off) #define WORK_LOOP 5000000   volatile BOOL g_fFinishedCalculation = FALSE;   DWORD WINAPI RecalcFunc(PVOID pvParam)  {     SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);     for (int i = 0; i < WORK_LOOP; i++);     printf("secondary thread done.\n");     g_fFinishedCalculation = TRUE;     return 0; }   DWORD WINAPI SomeOtherFunc(PVOID pvParam)  {     while (!g_fFinishedCalculation)         //Sleep(100);         ;     return 0; }   int main(int argc, char* argv[]) {       HANDLE hThread1 = (HANDLE)CreateThread(NULL, 0 , SomeOtherFunc, NULL, 0, NULL);     HANDLE hThread2 = (HANDLE)CreateThread(NULL, 0 , RecalcFunc, NULL, 0, NULL);           while (!g_fFinishedCalculation)         //Sleep(100);         ;     printf("primary thread done.\n");      CloseHandle(hThread1);     CloseHandle(hThread2);     return 0; }  你说对了，  作者的说法是扯淡，错误的。  我在你的代码基础修改一下；   #define WORK_LOOP 5000000  volatile BOOL g_fFinishedCalculation = FALSE;  DWORD WINAPI RecalcFunc(PVOID pvParam)  { 	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); 	for (int i = 0; i < WORK_LOOP; i++); 	printf("secondary thread done.\n"); 	g_fFinishedCalculation = TRUE; 	return 0; }  DWORD WINAPI SomeOtherFunc(PVOID pvParam)  { 	while (!g_fFinishedCalculation) 		SwitchToThread(); 	return 0; }  int main(int argc, char* argv[]) {  	HANDLE hThread1 = (HANDLE)CreateThread(NULL, 0 , SomeOtherFunc, NULL, 0, NULL); 	HANDLE hThread2 = (HANDLE)CreateThread(NULL, 0 , RecalcFunc, NULL, 0, NULL);  	while (!g_fFinishedCalculation) 		SwitchToThread(); 	printf("primary thread done.\n");  	CloseHandle(hThread1); 	CloseHandle(hThread2); 	return 0; }   这才是更好的做法

user: user-0
content: 引用 3 楼 angel_su 的回复:貌似只有你的测试的进程在跑，其他进程都处于等待，所以没有卡死。试试另外跑个while(1);进程，你的测试进程就会卡死，但是不要一直轮询sleep一下，优先继较低的子线程又会执行了...  现在才看到3楼有回复：  “貌似只有你的测试的进程在跑，其他进程都处于等待，所以没有卡死。试试另外跑个while(1);进程，你的测试进程就会卡死，但是不要一直轮询sleep一下，优先继较低的子线程又会执行了... ”  貌似只有你的测试的进程在跑，其他进程都处于等待，所以没有卡死  这是不可能的，你的依据是什么？    但是不要一直轮询sleep一下，优先继较低的子线程又会执行了  但是不要，  这是汉语的病句吗， 没有看懂，能不能说详细一点。

user: user-0
content: 引用 7 楼 angel_su 的回复:我在3楼说会卡死的类似下面代码，取消注释开启某一个sleep就能正常，感觉作者本意应改是在说用polling方法不妥，即便不卡死也浪费大量cpu时间...#include "stdafx.h" #include <Windows.h>  #pragma optimize("", off) #define WORK_LOOP 500000000  BOOL g_fFinishedCalculation = FALSE;  DWORD WINAPI RecalcFunc(PVOID pvParam)  { 	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); 	for (int i = 0; i < WORK_LOOP; i++) 			;     g_fFinishedCalculation = TRUE;     return 0; }  DWORD WINAPI SomeOtherFunc(PVOID pvParam)  { 	while (!g_fFinishedCalculation) 		//Sleep(100); 		;     return 0; }  int _tmain(int argc, _TCHAR* argv[]) {  	HANDLE hThread1 = (HANDLE)CreateThread(NULL, 0 , SomeOtherFunc, NULL, 0, NULL);     HANDLE hThread2 = (HANDLE)CreateThread(NULL, 0 , RecalcFunc, NULL, 0, NULL); 	 	while (!g_fFinishedCalculation) 		//Sleep(100); 		;     CloseHandle(hThread1); 	CloseHandle(hThread2); 	return 0; }   1. 你的代码测试实验环境是什么？    2.  你认为 作者的那段英文有问题吗？   用单核测试  ，如果你注释掉sleep后， 程序的唯一缺点：消耗cpu，但绝对不会导致子线程永远没有机会执行。 你不信的话，可以在 线程里加上 printf语句进行验证。

user: user-3
content: 我在3楼说会卡死的类似下面代码，取消注释开启某一个sleep就能正常，感觉作者本意应改是在说用polling方法不妥，即便不卡死也浪费大量cpu时间...#include "stdafx.h" #include <Windows.h>  #pragma optimize("", off) #define WORK_LOOP 500000000  BOOL g_fFinishedCalculation = FALSE;  DWORD WINAPI RecalcFunc(PVOID pvParam)  { 	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); 	for (int i = 0; i < WORK_LOOP; i++) 			;     g_fFinishedCalculation = TRUE;     return 0; }  DWORD WINAPI SomeOtherFunc(PVOID pvParam)  { 	while (!g_fFinishedCalculation) 		//Sleep(100); 		;     return 0; }  int _tmain(int argc, _TCHAR* argv[]) {  	HANDLE hThread1 = (HANDLE)CreateThread(NULL, 0 , SomeOtherFunc, NULL, 0, NULL);     HANDLE hThread2 = (HANDLE)CreateThread(NULL, 0 , RecalcFunc, NULL, 0, NULL); 	 	while (!g_fFinishedCalculation) 		//Sleep(100); 		;     CloseHandle(hThread1); 	CloseHandle(hThread2); 	return 0; }

user: user-2
content: 我觉得作者说的有问题，高优先级线程占用CPU时间多（比如一直轮询某个状态）并不代表低优先级线程得不到执行，抢先式多任务调度的处理方式是根据优先级和建立顺序进行排队，高优先级线程执行过一遍之后才轮到优先级低一档的线程执行。 当然作者说你不要一直轮询、要在循环中经常释放一点时间片，这肯定是好的编程习惯，可以降低CPU占用率、加快任务流转的速度、提高GUI的响应速度等等。 另外，7楼的代码是不会造成死循环的，只是因为他的代码子线程优先级低、循环时间长而已，把WORK_LOOP减小一下就可以看出来。比如这样（注意略加修改是为了便于使用非ms编译器通过，不影响代码执行效果）：  #include <stdio.h> #include <windows.h>   #pragma optimize("", off) #define WORK_LOOP 5000000   volatile BOOL g_fFinishedCalculation = FALSE;   DWORD WINAPI RecalcFunc(PVOID pvParam)  {     SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);     for (int i = 0; i < WORK_LOOP; i++);     printf("secondary thread done.\n");     g_fFinishedCalculation = TRUE;     return 0; }   DWORD WINAPI SomeOtherFunc(PVOID pvParam)  {     while (!g_fFinishedCalculation)         //Sleep(100);         ;     return 0; }   int main(int argc, char* argv[]) {       HANDLE hThread1 = (HANDLE)CreateThread(NULL, 0 , SomeOtherFunc, NULL, 0, NULL);     HANDLE hThread2 = (HANDLE)CreateThread(NULL, 0 , RecalcFunc, NULL, 0, NULL);           while (!g_fFinishedCalculation)         //Sleep(100);         ;     printf("primary thread done.\n");      CloseHandle(hThread1);     CloseHandle(hThread2);     return 0; }

user: user-0
content: 引用 3 楼 angel_su 的回复:貌似只有你的测试的进程在跑，其他进程都处于等待，所以没有卡死。试试另外跑个while(1);进程，你的测试进程就会卡死，但是不要一直轮询sleep一下，优先继较低的子线程又会执行了...  我发作者源代码： int WINAPI _tWinMain(...) {    CreateThread(..., RecalcFunc, ...);    ...    // Wait for the recalculation to complete.    while (!g_fFinishedCalculation)         ;    ... }  DWORD WINAPI RecalcFunc(PVOID pvParam) {    // Perform the recalculation.    ...    g_fFinishedCalculation = TRUE;    return(0); }

user: user-3
content: 貌似只有你的测试的进程在跑，其他进程都处于等待，所以没有卡死。试试另外跑个while(1);进程，你的测试进程就会卡死，但是不要一直轮询sleep一下，优先继较低的子线程又会执行了...

user: user-0
content: 高手帮忙顶一下

user: user-4
content: 不要迷信书、考题、老师、回帖； 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实！  有人说一套做一套，你相信他说的还是相信他做的？ 其实严格来说这个世界上古往今来所有人都是说一套做一套，不是吗？  不要写连自己也预测不了结果的代码！

user: user-0
content: 引用 1 楼 user-5 的回复:我觉得这个和操作系统的调度算法有关系。 如果调度是严格按照优先级的，只要有高优先级线程在运行，低优先级线程就没有机会，就会出现书上提到的情况。 而如果有基于时间片的调度算法，也就是说不管优先级高低都有机会运行(虽然可能机会有多有少)，那就不会出现这个状况了。  谢谢你的参与

user: user-5
content: 我觉得这个和操作系统的调度算法有关系。 如果调度是严格按照优先级的，只要有高优先级线程在运行，低优先级线程就没有机会，就会出现书上提到的情况。 而如果有基于时间片的调度算法，也就是说不管优先级高低都有机会运行(虽然可能机会有多有少)，那就不会出现这个状况了。