一道高难度的面试题(很怪)
//临界区变量
CRITICAL_SECTION m_sect;
int iCount = 0;
void Test(LPVOID iThread)
{
int Thread = (int ) iThread;
for(int i = 1 ; i<=10 ;i ++)
{
::EnterCriticalSection(&m_sect);
//Sleep(10);加上这条语句为什么结果不同
int j=iCount;
iCount++;
printf("Thread%d : icount= %d \n",Thread,iCount);
::LeaveCriticalSection(&m_sect);
}
}
//创建临界区
InitializeCriticalSection(&m_sect);
//创建线程
HANDLE hThread[3];
CWinThread* pT1=AfxBeginThread((AFX_THREADPROC)Test,(void*)1);
CWinThread* pT2=AfxBeginThread((AFX_THREADPROC)Test,(void*)2);
CWinThread* pT3=AfxBeginThread((AFX_THREADPROC)Test,(void*)3);
hThread[0]=pT1->m_hThread;
hThread[1]=pT2->m_hThread;
hThread[2]=pT3->m_hThread;
WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
问题
为什么结果不是30
问题点数:0、回复次数:42Top
1 楼Practise_Think(时代“过客”)回复于 2005-03-01 21:05:45 得分 0
为什么加了Sleep()之后就不同?? 没运行过,我也不明白!!!Top
2 楼lcs1980(lcs)回复于 2005-03-01 21:36:54 得分 0
那运行一下吧,
那你认为答案是多少?Top
3 楼ficher(小鱼儿)回复于 2005-03-01 22:11:11 得分 0
markTop
4 楼wwwllg(野蛮人)回复于 2005-03-01 23:03:28 得分 0
肯定是30.Top
5 楼duxianghe( dux++ )回复于 2005-03-01 23:11:12 得分 0
不太理解
应该是30吧Top
6 楼Caps77(厉兵秣马)回复于 2005-03-01 23:42:03 得分 0
没有sleepC41.7 256m机器为20,有sleep为30
猜解: 主线程在第三个线程启动后,进入函数体循环前退出了,第三个辅助线程来不及打印就退
出了,在程序末尾加个sleep(1),既为30
纯属猜测,关注正解!Top
7 楼baojian88888(机器人)回复于 2005-03-02 08:26:07 得分 0
gzTop
8 楼wizard13(我也要学习....)回复于 2005-03-02 08:37:28 得分 0
不懂,MARKTop
9 楼alec626(月吻长河Blog:spaces.msn.com/filebase)回复于 2005-03-02 08:58:05 得分 0
MARKTop
10 楼zhouhuaikun(怀空)回复于 2005-03-02 09:01:12 得分 0
markTop
11 楼vcleaner(我没当大哥很久了.......)回复于 2005-03-02 09:20:43 得分 0
呵呵,通过对话框的程序测试,在P4,2.1,256M机器上最终的结果都是30。没有什么不同的。Top
12 楼Magnus(小楼一夜听春雨)回复于 2005-03-02 10:06:23 得分 0
console下就和楼主所说一致。Top
13 楼babam()回复于 2005-03-02 10:06:45 得分 0
我在命令行下运行也是30. vc6.0;win2000; sleep(101);配置p4 2.4g,512mTop
14 楼mycs2005()回复于 2005-03-02 10:17:36 得分 0
我把其中的中间结果打印出来 结果如下
CRITICAL_SECTION m_sect;
int iCount = 0;
CString str;
DWORD Test(LPVOID iThread)
{
int Thread = (int ) iThread;
CFile f;
f.Open("c:\\olio.txt",CFile::modeWrite);
f.SeekToEnd();
CString s;
s.Format("-------------->thread:%d-> %d\r\n",Thread,iCount);
f.Write(s.GetBuffer(0),s.GetLength());
f.Close();
for(int i = 1 ; i<=10 ;i ++)
{
::EnterCriticalSection(&m_sect);
//Sleep(10);//加上这条语句为什么结果不同
int j=iCount;
CString s;
s.Format("thread:%d-> %d\r\n",Thread,iCount);
str += s;
iCount++;
::LeaveCriticalSection(&m_sect);
}
return Thread;
}
void CZTst10View::OnT1()
{
InitializeCriticalSection(&m_sect);
//创建线程
HANDLE hThread[3];
CWinThread* pT1=AfxBeginThread((AFX_THREADPROC)Test,(void*)1);
CWinThread* pT2=AfxBeginThread((AFX_THREADPROC)Test,(void*)2);
CWinThread* pT3=AfxBeginThread((AFX_THREADPROC)Test,(void*)3);
hThread[0]=pT1->m_hThread;
hThread[1]=pT2->m_hThread;
hThread[2]=pT3->m_hThread;
CString s;
CString s1;
DWORD dwret = -1;
::GetExitCodeThread(hThread[0],&dwret);
s1.Format("exit:%d",dwret);
s += s1;
::GetExitCodeThread(hThread[1],&dwret);
s1.Format("exit:%d",dwret);
s += s1;
::GetExitCodeThread(hThread[2],&dwret);
s1.Format("exit:%d",dwret);
s += s1;
WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
s1.Format("res:%d",iCount);
s+=s1;
iCount = 0;
AfxMessageBox(s);
CFile f;
f.Open("c:\\olio.txt",CFile::modeWrite);
f.SeekToEnd();
f.Write(str.GetBuffer(0),str.GetLength());
f.Close();
}
-------------->thread:1-> 0 //第一个线程进入时
-------------->thread:2-> 10 //第二个线程进入时
-------------->thread:3-> 0 //第三个线程进入时 奇怪这里怎么是 0应该是20啊
thread:1-> 0
thread:1-> 1
thread:1-> 2
thread:1-> 3
thread:1-> 4
thread:1-> 5
thread:1-> 6
thread:1-> 7
thread:1-> 8
thread:1-> 9
thread:2-> 10
thread:2-> 11
thread:2-> 12
thread:2-> 13
thread:2-> 14
thread:2-> 15
thread:2-> 16
thread:2-> 17
thread:2-> 18
thread:2-> 19
thread:3-> 0
thread:3-> 1
thread:3-> 2
thread:3-> 3
thread:3-> 4
thread:3-> 5
thread:3-> 6
thread:3-> 7
thread:3-> 8
thread:3-> 9
期待高手
Top
15 楼mycs2005()回复于 2005-03-02 10:18:10 得分 0
忘了说结果是20Top
16 楼wxdvc(csdn)回复于 2005-03-02 10:49:20 得分 0
加SLEEP==30
不加==20
我靠,期待高手.Top
17 楼wxdvc(csdn)回复于 2005-03-02 11:13:12 得分 0
经过多次测试,同意Caps77(厉兵秣马)的说法.Top
18 楼ndy_w(carpe diem)回复于 2005-03-02 11:22:31 得分 0
好玩...研究...
不加Sleep,在
hThread[0]=pT1->m_hThread;
hThread[1]=pT2->m_hThread;
hThread[2]=pT3->m_hThread;
时,有的线程已经结束,CWinThread会改变m_hThread。因此WaitForMultipleObjects失效。
如果
hThread[0]=(HANDLE)_beginthread(Test, 0, (void*)1);
hThread[1]=(HANDLE)_beginthread(Test, 0, (void*)2);
hThread[2]=(HANDLE)_beginthread(Test, 0, (void*)3);
即时不Sleep也不会错。Top
19 楼vcleaner(我没当大哥很久了.......)回复于 2005-03-02 11:44:18 得分 0
再次测试,控制台程序,在P4,2.1,256M机器上最终的结果都是30。没有什么不同的。Top
20 楼vcleaner(我没当大哥很久了.......)回复于 2005-03-02 11:46:59 得分 0
错了,是一个20,一个30。学习中。。。。。Top
21 楼nlstone(天外流星)回复于 2005-03-02 11:59:16 得分 0
同意楼上,这里线程结束后CWinThread会改变m_hThread,使之失效,继而wait失效
之所以没有打出30是因为第三个线程还来不及打印,程序就已经退出了
Top
22 楼ndy_w(carpe diem)回复于 2005-03-02 12:00:32 得分 0
问题应该是在AfxBeginThread上。创建第三个线程时,第一个线程结束了,AfxBeginThread返回pT3 == pT1Top
23 楼ppchen(韦古)回复于 2005-03-02 12:19:31 得分 0
C是没有进程和线程的概念。
C对多执行流的唯一支持是volatile。
int iCount = 0; --> int iCount = 0;
不想多说了,自己去查查volatile。
Top
24 楼ppchen(韦古)回复于 2005-03-02 12:20:29 得分 0
int iCount = 0; --> volatile int iCount = 0;
Top
25 楼little_duck(小鸭子)回复于 2005-03-02 12:55:49 得分 0
我经过反复测试 我的结果是30 不知道你为什么 我VC6.0 系统XPTop
26 楼wbusy(Woods)回复于 2005-03-02 13:32:09 得分 0
个人觉得是由于互斥和同步不同的原因,因为上面的CS只能保证互斥,而不能保证同步。
我把代码做了一点修改如下:
//
CCriticalSection g_cs;
int g_iCounter = 0;
CString g_str;
typedef enum{
READ = 1,
ADD,
SUB
}ACCESS;
int AccessCounter(ACCESS acc, int iThread = 0)
{
CSingleLock lock(&g_cs);
lock .Lock();
CString ss;
int iTemp;
switch(acc)
{
case ADD:
g_iCounter ++;
ss.Format("Thread %d ADD: %d\r\n",iThread, g_iCounter);
g_str += ss;
break;
case SUB:
g_iCounter --;
ss.Format("Thread %d SUB: %d\r\n",iThread, g_iCounter);
g_str += ss;
break;
case READ:
ss .Format("Thread %d Entered: %d\r\n", iThread, g_iCounter);
g_str += ss;
break;
}
iTemp = g_iCounter;
lock .Unlock();
return iTemp;
}
UINT Test(LPVOID lpvoid)
{
int Thread = (int ) lpvoid;
AccessCounter(READ, Thread);
for(int i = 1 ; i<=10 ;i ++)
{
//Sleep(GetTickCount() % 100);//加上这条语句为什么结果不同
AccessCounter(ADD, Thread);
}
return Thread;
}
void CTestDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知处理程序代码
g_iCounter = 0;
g_str = _T("");
//创建线程
HANDLE hThread[3];
CWinThread* pT1=AfxBeginThread((AFX_THREADPROC)Test,(void*)1, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
CWinThread* pT2=AfxBeginThread((AFX_THREADPROC)Test,(void*)2, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
CWinThread* pT3=AfxBeginThread((AFX_THREADPROC)Test,(void*)3, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
ASSERT_VALID(pT1);
ASSERT_VALID(pT2);
ASSERT_VALID(pT3);
hThread[0]=pT1->m_hThread;
hThread[1]=pT2->m_hThread;
hThread[2]=pT3->m_hThread;
pT1 ->ResumeThread();
pT2 ->ResumeThread();
pT3 ->ResumeThread();
WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
CString s;
CString s1;
DWORD dwret = -1;
::GetExitCodeThread(hThread[0],&dwret);
s1.Format("exit:%d ",dwret);
s += s1;
::GetExitCodeThread(hThread[1],&dwret);
s1.Format("exit:%d ",dwret);
s += s1;
::GetExitCodeThread(hThread[2],&dwret);
s1.Format("exit:%d ",dwret);
s += s1;
s1.Format("Counter after 3 threads exited: %d", AccessCounter(READ));
s+=s1;
AfxMessageBox(s);
g_str += s;
CFile f;
f.Open("D:\\olio.txt",CFile::modeWrite | CFile ::modeCreate);
f.Write(g_str.GetBuffer(0), g_str.GetLength());
f.Close();
}
测试结果(随机的)分别如下:
1)没有Sleep
Thread 3 Entered: 0
Thread 3 ADD: 1
Thread 3 ADD: 2
Thread 3 ADD: 3
Thread 3 ADD: 4
Thread 3 ADD: 5
Thread 3 ADD: 6
Thread 3 ADD: 7
Thread 3 ADD: 8
Thread 3 ADD: 9
Thread 3 ADD: 10
Thread 1 Entered: 10
Thread 1 ADD: 11
Thread 1 ADD: 12
Thread 1 ADD: 13
Thread 1 ADD: 14
Thread 1 ADD: 15
Thread 1 ADD: 16
Thread 1 ADD: 17
Thread 1 ADD: 18
Thread 1 ADD: 19
Thread 1 ADD: 20
Thread 2 Entered: 20
Thread 2 ADD: 21
Thread 2 ADD: 22
Thread 2 ADD: 23
Thread 2 ADD: 24
Thread 2 ADD: 25
Thread 2 ADD: 26
Thread 2 ADD: 27
Thread 2 ADD: 28
Thread 2 ADD: 29
Thread 2 ADD: 30
Thread 0 Entered: 30
exit:-1 exit:-1 exit:-1 Counter after 3 threads exited: 30
2)有Sleep
Thread 3 Entered: 0
Thread 1 Entered: 0
Thread 2 Entered: 0
Thread 3 ADD: 1
Thread 1 ADD: 2
Thread 2 ADD: 3
Thread 3 ADD: 4
Thread 1 ADD: 5
Thread 2 ADD: 6
Thread 3 ADD: 7
Thread 1 ADD: 8
Thread 2 ADD: 9
Thread 3 ADD: 10
Thread 3 ADD: 11
Thread 3 ADD: 12
Thread 3 ADD: 13
Thread 1 ADD: 14
Thread 2 ADD: 15
Thread 3 ADD: 16
Thread 1 ADD: 17
Thread 2 ADD: 18
Thread 3 ADD: 19
Thread 3 ADD: 20
Thread 1 ADD: 21
Thread 2 ADD: 22
Thread 1 ADD: 23
Thread 2 ADD: 24
Thread 1 ADD: 25
Thread 2 ADD: 26
Thread 1 ADD: 27
Thread 2 ADD: 28
Thread 1 ADD: 29
Thread 2 ADD: 30
Thread 0 Entered: 30
exit:-1 exit:-1 exit:-1 Counter after 3 threads exited: 30
上面的结果应该可以说明问题。Top
27 楼michael1081(云飞扬)回复于 2005-03-02 13:35:57 得分 0
不懂,gzTop
28 楼windbells(风铃)回复于 2005-03-02 14:02:55 得分 0
这个问题你只要研究一下MFC的源码就明白了AfxBeginThread调用后,会创建一个CWinThread对象,然后用beginthreadex创建一个线程,线程创建成功后,返回CWinThread指针。
问题就出在MFC的内部处理上,在
UINT APIENTRY _AfxThreadEntry(void* pParam)
的函数结尾处会调用
AfxEndThread(nResult);
而
void AFXAPI AfxEndThread(UINT nExitCode, BOOL bDelete)
{
#ifndef _MT
nExitCode;
bDelete;
#else
// remove current CWinThread object from memory
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
CWinThread* pThread = pState->m_pCurrentWinThread;
if (pThread != NULL)
{
ASSERT_VALID(pThread);
ASSERT(pThread != AfxGetApp());
// cleanup OLE if required
if (pThread->m_lpfnOleTermOrFreeLib != NULL)
(*pThread->m_lpfnOleTermOrFreeLib)(TRUE, FALSE);
if (bDelete)
pThread->Delete();
pState->m_pCurrentWinThread = NULL;
}
// allow cleanup of any thread local objects
AfxTermThread();
// allow C-runtime to cleanup, and exit the thread
_endthreadex(nExitCode);
#endif //!_MT
}
bDelete缺省是TRUE
所以会调用CWinThread的delete 函数
void CWinThread::Delete()
{
// delete thread if it is auto-deleting
if (m_bAutoDelete)
delete this;
}
m_bAutoDelete缺省也是TRUE
所以执行的最后CwinThread会把自身删除掉,包括关掉线程句柄。
直接用SDK开发,就没有这个问题了。如果用MFC的话,可以这么处理
InitializeCriticalSection(&m_sect);
//创建线程
HANDLE hThread[3];
CWinThread* pT1=AfxBeginThread((AFX_THREADPROC)Test,(void*)1,0,0,CREATE_SUSPENDED);
CWinThread* pT2=AfxBeginThread((AFX_THREADPROC)Test,(void*)2,0,0,CREATE_SUSPENDED);
CWinThread* pT3=AfxBeginThread((AFX_THREADPROC)Test,(void*)3,0,0,CREATE_SUSPENDED);
pT1->m_bAutoDelete = FALSE;
pT2->m_bAutoDelete = FALSE;
pT3->m_bAutoDelete = FALSE;
hThread[0]=pT1->m_hThread;
hThread[1]=pT2->m_hThread;
hThread[2]=pT3->m_hThread;
pT1->ResumeThread();
pT2->ResumeThread();
pT3->ResumeThread();
DWORD dwRet = WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
delete pT1;
delete pT2;
delete pT3;
Top
29 楼guihui5460( flystar)回复于 2005-03-02 15:27:22 得分 0
WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
这函数返回是失败的,我想这是原因所在,当这个函数返回返加时,thread3还没有运行
而下面的s1.Format("res:%d",iCount);
s+=s1;
iCount = 0;
AfxMessageBox(s);
所以会出现那样的结果,当运行第三线程时iCount已经为0了,
我自己认为WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
之所以会返回失败是因为当调用它时,thread1,thread2,已运行结束,而且线程运行完这后自动删除了,所以会返回失败,而加Sleep(10)Top
30 楼guihui5460( flystar)回复于 2005-03-02 15:28:53 得分 0
后,WaitForMultipleObjects(3,hThread,TRUE,INFINITE);运行时,thread1,thread2还没有运行结束,对象存在,返回成功,所以结果是正确的
Top
31 楼andyfr1210(华仔)回复于 2005-03-02 17:36:38 得分 0
在控制台下测试,不管加不加SLEEP都返回30
不知道是不是我的电脑比较烂的原因!
Top
32 楼SInoyew(天行杨)回复于 2005-03-02 18:20:51 得分 0
哦?Top
33 楼Featured(我握着爱情的门票静静排队……)回复于 2005-03-02 19:08:30 得分 0
天哪,我晕了
怎么我拷贝代码建立工程Build发生错误,
如下:
#include <stdio.h>
CRITICAL_SECTION m_sect;
int iCount = 0;
void Test(LPVOID iThread)
{
int Thread = (int ) iThread;
for(int i = 1 ; i<=10 ;i ++)
{
::EnterCriticalSection(&m_sect);
//Sleep(10);加上这条语句为什么结果不同
int j=iCount;
iCount++;
printf("Thread%d : icount= %d \n",Thread,iCount);
::LeaveCriticalSection(&m_sect);
}
}
void main()
{
//////////////////////////////////////////////////////////
//创建临界区
InitializeCriticalSection(&m_sect);
//创建线程
HANDLE hThread[3];
CWinThread* pT1=AfxBeginThread((AFX_THREADPROC)Test,(void*)1);
CWinThread* pT2=AfxBeginThread((AFX_THREADPROC)Test,(void*)2);
CWinThread* pT3=AfxBeginThread((AFX_THREADPROC)Test,(void*)3);
hThread[0]=pT1->m_hThread;
hThread[1]=pT2->m_hThread;
hThread[2]=pT3->m_hThread;
WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
}
Top
34 楼Anthony88(Anthony)回复于 2005-03-02 20:31:24 得分 0
太难了,MFC太难了,放弃了
也看不懂了Top
35 楼redchina(风清云淡)回复于 2005-03-02 21:17:39 得分 0
for(int i = 1 ; i<=10 ;i ++)
{
::EnterCriticalSection(&m_sect);
//Sleep(10);加上这条语句为什么结果不同
int j=iCount;
iCount++;
printf("Thread%d : icount= %d \n",Thread,iCount);
::LeaveCriticalSection(&m_sect);
}
修改成下边的呢?
::EnterCriticalSection(&m_sect);
for(int i = 1 ; i<=10 ;i ++)
{
//Sleep(10);加上这条语句为什么结果不同
int j=iCount;
iCount++;
printf("Thread%d : icount= %d \n",Thread,iCount);
}
::LeaveCriticalSection(&m_sect);Top
36 楼redchina(风清云淡)回复于 2005-03-02 21:19:52 得分 0
你所列出的代码事实上线程切换会非常频繁。Top
37 楼zengwujun(月之海 为linux入门奋斗100天)回复于 2005-03-03 10:37:52 得分 0
markTop
38 楼sashilover(闭门思过中。。。。)回复于 2005-03-03 13:10:17 得分 0
markTop
39 楼winthegame(120斤重的大青蛙)回复于 2005-03-03 14:14:51 得分 0
要有根据,最简单就是加些调试!
DWORD dwResult = WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
printf("%d\n", dwResult);
printf("%d", GetLastError());
可以明显看出WaitForMultipleObjects返回是以失效来结束的,而并非是返回成功。而GetLastError也返回错误。查查错误原因,就清楚了,由于没有Sleep的存在,线程循环几乎是以单线程似的方式串行运行,在第三个线程压根没有分配到时间启动前,时间已经轮循到了主线程那里,这时执行
WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
WaitForMultipleObjects立即发现分配过来的第三个线程句柄为无效句柄,于是不作等待,立即返回,
就这样默默的结束了整个程序,留下了一屁股后遗症。
所以找到症结,要正确,在任何线程中加Sleep均可,既是在做WaitForMultipleObjects 之前就等待即可,或者干脆点说,把主线程代码量加大,只要保证线程在启动完毕前,主线程不莫名其妙的Over就可以了。Top
40 楼stonex_2000(三棱镜)回复于 2005-03-03 15:13:43 得分 0
正如ndy_w(carpe diem)说的那样,
有可能在运行到给数组赋值之前线程就运行完毕了.
如下就不管是否sleep就没问题了.
hThread[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Test, (void*)1, 0, NULL);
hThread[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Test, (void*)2, 0, NULL);
hThread[2] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Test, (void*)3, 0, NULL);Top
41 楼dongfa(一桶江湖( http://www.codelive.net ))回复于 2005-03-03 15:54:04 得分 0
因为启动线程是异步的,如果运行的太快,线程还没有启动完成时,线程HANDLE肯定是无效的,因此在执行WaitForMultipleObjects时变反回了。Top
42 楼shizhen(失贞)回复于 2005-03-03 18:47:17 得分 0
执行到CWinThread* pT3=AfxBeginThread((AFX_THREADPROC)Test,(void*)3);时第一个线程已经执行完,并且释放了所用的内存,pT3这时正好申请到了pT1用过的内存.地址相同.
到WaitForMultipleObjects,检查到两个相同地址的句柄值,即而失败返回Top




