线程 时间片 问题(From 孙鑫 vc++)

时间:2021-10-11 08:33:24
15 章 线程同步 火车站售票系统模拟程序

#include <windows.h>
#include <iostream.h>

DWORD WINAPI Thread1(LPVOID lpParameter);
DWORD WINAPI Thread2(LPVOID lpParameter);

int tickets=100;
HANDLE g_Mutex;

void main()
{
g_Mutex=CreateMutex(NULL,false,NULL);

HANDLE hThread1=CreateThread(NULL,0,Thread1,NULL,0,NULL);
HANDLE hThread2=CreateThread(NULL,0,Thread2,NULL,0,NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);

Sleep(2000);
return;
}

DWORD WINAPI Thread1(LPVOID lpParameter)
{
while (true)
{
WaitForSingleObject(g_Mutex,INFINITE);
if (tickets>0)
{
Sleep(1);
cout<<"Thread 1 sells a ticket   :   "<<tickets--<<endl;
}
else
break;
ReleaseMutex(g_Mutex);
}

return 0;
}

DWORD WINAPI Thread2(LPVOID lpParameter)
{
while (true)
{
WaitForSingleObject(g_Mutex,INFINITE);
if (tickets>0)
{
Sleep(1);
cout<<"Thread 2 sells a ticket : "<<tickets--<<endl;
}
else
break;
ReleaseMutex(g_Mutex);
}

return 0;
}

如上代码:
当 2个线程中 没有‘Sleep(1);’时, 2个线程 交替各卖一段时间票(连续卖数张) 直至结束。
当 2个线程中 ‘Sleep(1);’时,为何就没有‘一个线程 连续卖票’的情况了?而是你卖一张我卖一张直至结束?
我的想法是:假如 现在 是Thread1等到了 互斥对象,执行到‘Sleep(1);’时,Thread1睡觉(不占用CPU时间),Thread2执行(只能执行到WaitForSingleObject);然后Thread1睡醒,Thread1执行到‘ReleaseMutex(g_Mutex);’,为何接下来 必定 是Thread2得到互斥对象?        我的意思是:Thread1执行到‘ReleaseMutex(g_Mutex);’,虽然Thread2在等 互斥对象 ,但是可能Thread1的时间片还没结束,Thread1又运行了代码‘WaitForSingleObject’并得到了 互斥对象。这种情况不可能吗?

环境:虚拟机:单核 2003 server Enterprise edition sp2

16 个解决方案

#1


你用sleep(0)试试!还有,建议sleep不要放在释放互斥对象后面!即放在ReleaseMutex(g_Mutex);后面!

#2


sleep(0)有什么用?

#3


这个涉及到操作系统内部的线程调度机制。。。。
理论上来说,你说的情况是可能出现的,也就是Thread1在ReleaseMutex(g_Mutex);以后,仍然自己得到mutex继续执行。
但是在这之前,Thread2比它先WaitForSingleObject对不对?
系统维护了一个线程内核对象,系统会根据线程的等待时间来 
提高起权限级别,所以使得所有的线程都有机会得到调度。 
(就好比一个卖饭的窗口前有A.B两列队伍(极度饥饿),A队第一个人买完饭了,而你是B队伍中的第一个,当A队第一个买完了,是不是该你买了呢?不然继续到A队第二个人去买,那你们B队的人恐怕都不干了哦。。。。假设A队人数足够多,那你们整个B队都有被饿死的可能)
所以说,系统这么调度是为了防止线程饥饿~~
就因为这个原因,系统会将Thread2的优先级提高,所以会交替执行……

#4


操作系统分给线程的时间片足够你的线程做很多次循环,所以不加 Sleep 会出现一个线程连续出票的情况

Sleep 函数可以让线程主动放弃一个时间片的后续时间,因而加了 Sleep 以后就可以轮流了

使用 Sleep(0) 也可以, 但最好是放在 ReleaseMutex 后面, 不然会浪费CPU时间

#5


我想要的是 类似 3楼的解释, 并不是说要 避免我说的情况的方案


引用 3 楼 yihan7h 的回复:
这个涉及到操作系统内部的线程调度机制。。。。 
理论上来说,你说的情况是可能出现的,也就是Thread1在ReleaseMutex(g_Mutex);以后,仍然自己得到mutex继续执行。 
但是在这之前,Thread2比它先WaitForSingleObject对不对? 
系统维护了一个线程内核对象,系统会根据线程的等待时间来 
提高起权限级别,所以使得所有的线程都有机会得到调度。 
(就好比一个卖饭的窗口前有A.B两列队伍(极度饥饿),A队第一个人买完饭了,而…

那就这个程序来讲的话,假如 Thread1 执行到‘ReleaseMutex(g_Mutex);’后时间片还没完,继续执行到‘WaitForSingleObject(g_Mutex,INFINITE);’,这是 由于 互斥对象  等待时间的关系CPU选择让Thread2来运行,而让Thread1休息(虽然Thread1的时间片还没完)。    可以这样理解不???

#6


Sleep()就是让出当前CPU,这样其他的线程有机会抢到CPU,从而可以让其他线程工作,如果不Sleep就有可能被某个线程一直占用CPU,其他线程得不到处理
Sleep(0),是一种优化,还是可能让出CPU的...

#7


引用 6 楼 oyljerry 的回复:
Sleep()就是让出当前CPU,这样其他的线程有机会抢到CPU,从而可以让其他线程工作,如果不Sleep就有可能被某个线程一直占用CPU,其他线程得不到处理 
Sleep(0),是一种优化,还是可能让出CPU的...

那 Sleep(0)或Sleep(1),对于Thread2来说效果应该没差吧?

另外,看下我5楼的问题呢?

#8


时间片应该只能保证执行一条汇编代码吧?应该是一个很小的单位,不存在“时间片还没完”的情况吧

#9


引用 8 楼 hust_terry 的回复:
时间片应该只能保证执行一条汇编代码吧?应该是一个很小的单位,不存在“时间片还没完”的情况吧

你这就主观了吧,你吧上面代码,找个 单核的系统 跑下(去掉里面的sleep),看下就知道了。

#10


Thread1执行到‘ReleaseMutex(g_Mutex);’,虽然Thread2在等 互斥对象 ,但是可能Thread1的时间片还没结束,Thread1又运行了代码‘WaitForSingleObject’并得到了 互斥对象。这种情况不可能吗? 

不可能,线程2早就在等g_Mutex,只要线程1一释放,不管他还有没有时间片,线程2就获得该g_Mutex,所以即使线程1有时间片,
接下来就换成线程1被卡在waifor

#11


之所以要有sleep() 是不让线程1一开始就可以执行很多次循环,因为线程1一到sleep必定让出时间片,为的就是让线程2

早早的等着g_Mutex,这样一旦线程1一释放互斥对象,线程2必定获得该对象,这时就换线程1被卡了.

而没有sleep,线程1一开始就可以执行很多次,直到时间片玩,才轮到线程2

#12


看了 3、10、11楼,算是比较清楚了




对于10、11楼,czcwzw的解释
那上面代码用下面替换:
1、事件对象:‘CreateEvent(NULL,false,false,NULL);’+‘WaitForSingleObject’+‘SetEvent’
2、关键代码段:‘InitializeCriticalSection’+‘EnterCriticalSection’+‘LeaveCriticalSection’
应该是一样的吧???

#13


学习了

#14


引用 5 楼 okmnji79513 的回复:
我想要的是 类似 3楼的解释, 并不是说要 避免我说的情况的方案 


引用 3 楼 yihan7h 的回复:
这个涉及到操作系统内部的线程调度机制。。。。 
理论上来说,你说的情况是可能出现的,也就是Thread1在ReleaseMutex(g_Mutex);以后,仍然自己得到mutex继续执行。 
但是在这之前,Thread2比它先WaitForSingleObject对不对? 
系统维护了一个线程内核对象,系统会根据线程的等待时间来 
提高起权限级别,所以使得所有的线程都…

恩,简单的说,你可以这样理解:当thread1 ReleaseMutex后,系统会将其优先级降低,然后执行到waitforsingleobject,而之前thread2已经先行waitforsingleobject,优先级已经被提升,当两个线程都waitforsingleobject时,就由系统来决定由哪个线程获得mutex,这个时候根据优先级系统会给thread2。(尽管此时thread1的时间片没有用完)

1、事件对象:‘CreateEvent(NULL,false,false,NULL);’+‘WaitForSingleObject’+‘SetEvent’ 
2、关键代码段:‘InitializeCriticalSection’+‘EnterCriticalSection’+‘LeaveCriticalSection’ 
应该是一样的吧???

对,是一样的,防止线程饥饿,操作系统的线程调度算法也和上述一样。
LZ可以试试下面的代码:
#include <windows.h>
#include <iostream>

DWORD WINAPI Thread1(LPVOID lpParameter);
DWORD WINAPI Thread2(LPVOID lpParameter);
using namespace std;
int tickets=100;
HANDLE g_Mutex;
CRITICAL_SECTION CS;
void main()
{
g_Mutex=CreateMutex(NULL,false,NULL);
InitializeCriticalSection(&CS);
HANDLE hThread1=CreateThread(NULL,0,Thread1,NULL,0,NULL);
HANDLE hThread2=CreateThread(NULL,0,Thread2,NULL,0,NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);

Sleep(2000);
return;
}

DWORD WINAPI Thread1(LPVOID lpParameter)
{
while (true)
{

EnterCriticalSection(&CS);
if (tickets>0)
{
Sleep(1);
cout<<"Thread 1 sells a ticket   :   "<<tickets--<<endl;
}
else
break;

LeaveCriticalSection(&CS);
}

return 0;
}

DWORD WINAPI Thread2(LPVOID lpParameter)
{
while (true)
{
EnterCriticalSection(&CS);

if (tickets>0)
{
Sleep(1);
cout<<"Thread 2 sells a ticket : "<<tickets--<<endl;
}
else
break;
LeaveCriticalSection(&CS);
}

return 0;
}

#15


WaitForSingleObject等待对象时,会检查是否有其它线程排在自己前面等待,如果有,则让对方优先获取。上面代码中的Sleep会使当前线程主动放弃时间片,从而使得另一线程首先进入等待状态。

#16


学习了~~

#1


你用sleep(0)试试!还有,建议sleep不要放在释放互斥对象后面!即放在ReleaseMutex(g_Mutex);后面!

#2


sleep(0)有什么用?

#3


这个涉及到操作系统内部的线程调度机制。。。。
理论上来说,你说的情况是可能出现的,也就是Thread1在ReleaseMutex(g_Mutex);以后,仍然自己得到mutex继续执行。
但是在这之前,Thread2比它先WaitForSingleObject对不对?
系统维护了一个线程内核对象,系统会根据线程的等待时间来 
提高起权限级别,所以使得所有的线程都有机会得到调度。 
(就好比一个卖饭的窗口前有A.B两列队伍(极度饥饿),A队第一个人买完饭了,而你是B队伍中的第一个,当A队第一个买完了,是不是该你买了呢?不然继续到A队第二个人去买,那你们B队的人恐怕都不干了哦。。。。假设A队人数足够多,那你们整个B队都有被饿死的可能)
所以说,系统这么调度是为了防止线程饥饿~~
就因为这个原因,系统会将Thread2的优先级提高,所以会交替执行……

#4


操作系统分给线程的时间片足够你的线程做很多次循环,所以不加 Sleep 会出现一个线程连续出票的情况

Sleep 函数可以让线程主动放弃一个时间片的后续时间,因而加了 Sleep 以后就可以轮流了

使用 Sleep(0) 也可以, 但最好是放在 ReleaseMutex 后面, 不然会浪费CPU时间

#5


我想要的是 类似 3楼的解释, 并不是说要 避免我说的情况的方案


引用 3 楼 yihan7h 的回复:
这个涉及到操作系统内部的线程调度机制。。。。 
理论上来说,你说的情况是可能出现的,也就是Thread1在ReleaseMutex(g_Mutex);以后,仍然自己得到mutex继续执行。 
但是在这之前,Thread2比它先WaitForSingleObject对不对? 
系统维护了一个线程内核对象,系统会根据线程的等待时间来 
提高起权限级别,所以使得所有的线程都有机会得到调度。 
(就好比一个卖饭的窗口前有A.B两列队伍(极度饥饿),A队第一个人买完饭了,而…

那就这个程序来讲的话,假如 Thread1 执行到‘ReleaseMutex(g_Mutex);’后时间片还没完,继续执行到‘WaitForSingleObject(g_Mutex,INFINITE);’,这是 由于 互斥对象  等待时间的关系CPU选择让Thread2来运行,而让Thread1休息(虽然Thread1的时间片还没完)。    可以这样理解不???

#6


Sleep()就是让出当前CPU,这样其他的线程有机会抢到CPU,从而可以让其他线程工作,如果不Sleep就有可能被某个线程一直占用CPU,其他线程得不到处理
Sleep(0),是一种优化,还是可能让出CPU的...

#7


引用 6 楼 oyljerry 的回复:
Sleep()就是让出当前CPU,这样其他的线程有机会抢到CPU,从而可以让其他线程工作,如果不Sleep就有可能被某个线程一直占用CPU,其他线程得不到处理 
Sleep(0),是一种优化,还是可能让出CPU的...

那 Sleep(0)或Sleep(1),对于Thread2来说效果应该没差吧?

另外,看下我5楼的问题呢?

#8


时间片应该只能保证执行一条汇编代码吧?应该是一个很小的单位,不存在“时间片还没完”的情况吧

#9


引用 8 楼 hust_terry 的回复:
时间片应该只能保证执行一条汇编代码吧?应该是一个很小的单位,不存在“时间片还没完”的情况吧

你这就主观了吧,你吧上面代码,找个 单核的系统 跑下(去掉里面的sleep),看下就知道了。

#10


Thread1执行到‘ReleaseMutex(g_Mutex);’,虽然Thread2在等 互斥对象 ,但是可能Thread1的时间片还没结束,Thread1又运行了代码‘WaitForSingleObject’并得到了 互斥对象。这种情况不可能吗? 

不可能,线程2早就在等g_Mutex,只要线程1一释放,不管他还有没有时间片,线程2就获得该g_Mutex,所以即使线程1有时间片,
接下来就换成线程1被卡在waifor

#11


之所以要有sleep() 是不让线程1一开始就可以执行很多次循环,因为线程1一到sleep必定让出时间片,为的就是让线程2

早早的等着g_Mutex,这样一旦线程1一释放互斥对象,线程2必定获得该对象,这时就换线程1被卡了.

而没有sleep,线程1一开始就可以执行很多次,直到时间片玩,才轮到线程2

#12


看了 3、10、11楼,算是比较清楚了




对于10、11楼,czcwzw的解释
那上面代码用下面替换:
1、事件对象:‘CreateEvent(NULL,false,false,NULL);’+‘WaitForSingleObject’+‘SetEvent’
2、关键代码段:‘InitializeCriticalSection’+‘EnterCriticalSection’+‘LeaveCriticalSection’
应该是一样的吧???

#13


学习了

#14


引用 5 楼 okmnji79513 的回复:
我想要的是 类似 3楼的解释, 并不是说要 避免我说的情况的方案 


引用 3 楼 yihan7h 的回复:
这个涉及到操作系统内部的线程调度机制。。。。 
理论上来说,你说的情况是可能出现的,也就是Thread1在ReleaseMutex(g_Mutex);以后,仍然自己得到mutex继续执行。 
但是在这之前,Thread2比它先WaitForSingleObject对不对? 
系统维护了一个线程内核对象,系统会根据线程的等待时间来 
提高起权限级别,所以使得所有的线程都…

恩,简单的说,你可以这样理解:当thread1 ReleaseMutex后,系统会将其优先级降低,然后执行到waitforsingleobject,而之前thread2已经先行waitforsingleobject,优先级已经被提升,当两个线程都waitforsingleobject时,就由系统来决定由哪个线程获得mutex,这个时候根据优先级系统会给thread2。(尽管此时thread1的时间片没有用完)

1、事件对象:‘CreateEvent(NULL,false,false,NULL);’+‘WaitForSingleObject’+‘SetEvent’ 
2、关键代码段:‘InitializeCriticalSection’+‘EnterCriticalSection’+‘LeaveCriticalSection’ 
应该是一样的吧???

对,是一样的,防止线程饥饿,操作系统的线程调度算法也和上述一样。
LZ可以试试下面的代码:
#include <windows.h>
#include <iostream>

DWORD WINAPI Thread1(LPVOID lpParameter);
DWORD WINAPI Thread2(LPVOID lpParameter);
using namespace std;
int tickets=100;
HANDLE g_Mutex;
CRITICAL_SECTION CS;
void main()
{
g_Mutex=CreateMutex(NULL,false,NULL);
InitializeCriticalSection(&CS);
HANDLE hThread1=CreateThread(NULL,0,Thread1,NULL,0,NULL);
HANDLE hThread2=CreateThread(NULL,0,Thread2,NULL,0,NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);

Sleep(2000);
return;
}

DWORD WINAPI Thread1(LPVOID lpParameter)
{
while (true)
{

EnterCriticalSection(&CS);
if (tickets>0)
{
Sleep(1);
cout<<"Thread 1 sells a ticket   :   "<<tickets--<<endl;
}
else
break;

LeaveCriticalSection(&CS);
}

return 0;
}

DWORD WINAPI Thread2(LPVOID lpParameter)
{
while (true)
{
EnterCriticalSection(&CS);

if (tickets>0)
{
Sleep(1);
cout<<"Thread 2 sells a ticket : "<<tickets--<<endl;
}
else
break;
LeaveCriticalSection(&CS);
}

return 0;
}

#15


WaitForSingleObject等待对象时,会检查是否有其它线程排在自己前面等待,如果有,则让对方优先获取。上面代码中的Sleep会使当前线程主动放弃时间片,从而使得另一线程首先进入等待状态。

#16


学习了~~