none
关键代码段CriticalSection RRS feed

  • 问题

  • 正在学习孙鑫老师的C++视频教程(6.0环境),但因为我用的是vs.net2010,电脑的环境有所不同,win7/双核4线程。

    我写出代码后,不知道为什么只有一个线程在sell ticket;而且退出时发生错误(Critical.exe 中的 0x77098dc9 处有未经处理的异常: 0xC0000005: 写入位置 0x00000014 时发生访问冲突)

    #include <Windows.h>
    #include <iostream>
    #include <tchar.h>
    int fun1Proc();
    DWORD WINAPI Fun1Proc(LPVOID lpParameter);
    int fun2Proc();
    DWORD WINAPI Fun2Proc(LPVOID lpParameter);
    int nticket=100;
    CRITICAL_SECTION g_criticalSection;
    void main()
    {
    	HANDLE hThread1=CreateThread(0,0,&Fun1Proc,0,0,0);
    	HANDLE hThread2=CreateThread(0,0,&Fun2Proc,0,0,0);
    	CloseHandle(hThread1);
    	CloseHandle(hThread2);
    	InitializeCriticalSection(&g_criticalSection);
    	Sleep(4000);
    	DeleteCriticalSection(&g_criticalSection);
    }
    DWORD WINAPI Fun1Proc(LPVOID lpParameter)
    {
    	while(true)
    	{
    		EnterCriticalSection(&g_criticalSection);
    		if(nticket>0)
    		{
    			Sleep(1);
    			std::cout<<"thread 1 sell ticket "<<nticket--<<std::endl;
    		}
    		LeaveCriticalSection(&g_criticalSection);
    	}
    	return 0;
    }
    DWORD WINAPI Fun2Proc(LPVOID lpParameter)
    {
    	while(true)
    	{
    		EnterCriticalSection(&g_criticalSection);
    		if(nticket>0)
    		{
    			Sleep(1);
    			std::cout<<"thread 2 sell ticket "<<nticket--<<std::endl;
    		}
    		LeaveCriticalSection(&g_criticalSection);
    	}
    	return 0;
    }






    2012年11月11日 9:28

答案

  • 只有一个线程售票,因为线程 1 的上一个离开和下一个进入挨得太紧,估计轮不上另外一个线程进入。在离开关键段后再调用 Sleep。

    #include <windows.h>
    #include <stdio.h>
    
    int g_nTickets = 100;
    CRITICAL_SECTION g_cs;
    
    DWORD WINAPI Thread(LPVOID p) {
        for (;;) {
            EnterCriticalSection(&g_cs);
    
            if (g_nTickets > 0)
                printf("thread %d %d\n", p, g_nTickets--);
            else {
                LeaveCriticalSection(&g_cs);
                break;
            }
    
            LeaveCriticalSection(&g_cs);
            Sleep(1);
        }
    
        return 0;
    }
    
    int main() {
        HANDLE h1, h2;
    
        InitializeCriticalSection(&g_cs);
        h1 = CreateThread(0, 0, Thread, (LPVOID)1, 0, 0);
        h2 = CreateThread(0, 0, Thread, (LPVOID)2, 0, 0);
    
        WaitForSingleObject(h1, INFINITE);
        WaitForSingleObject(h2, INFINITE);
    
        DeleteCriticalSection(&g_cs);
        CloseHandle(h1);
        CloseHandle(h2);
    }

    退出时有异常,因为你的线程函数从不退出,当 main 中删除了关键段时,两个线程函数再访问关键段就有异常。

    2012年11月11日 20:41
  • #include <Windows.h>
    #include <iostream>
    #include <tchar.h>
    int fun1Proc();
    DWORD WINAPI Fun1Proc(LPVOID lpParameter);
    int fun2Proc();
    DWORD WINAPI Fun2Proc(LPVOID lpParameter);
    int nticket=100;
    CRITICAL_SECTION g_criticalSection;
    void main()
    {
    	InitializeCriticalSection(&g_criticalSection);
    	HANDLE hThread[2];
    	hThread[0]=CreateThread(0,0,&Fun1Proc,0,0,0);
    	hThread[1]=CreateThread(0,0,&Fun2Proc,0,0,0);
    	
    	WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
    	CloseHandle(hThread[0]);
    	CloseHandle(hThread[1]);
    	DeleteCriticalSection(&g_criticalSection);
    }
    DWORD WINAPI Fun1Proc(LPVOID lpParameter)
    {
    	while(true)
    	{
    		EnterCriticalSection(&g_criticalSection);
    		if(nticket>0)
    		{
    			Sleep(1);
    			std::cout<<"thread 1 sell ticket "<<nticket--<<std::endl;
    		}
    		else
    		{
    			LeaveCriticalSection(&g_criticalSection);
    			break;
    		}
    		LeaveCriticalSection(&g_criticalSection);
    	}
    	return 0;
    }
    DWORD WINAPI Fun2Proc(LPVOID lpParameter)
    {
    	while(true)
    	{
    		EnterCriticalSection(&g_criticalSection);
    		if(nticket>0)
    		{
    			Sleep(1);
    			std::cout<<"thread 2 sell ticket "<<nticket--<<std::endl;
    		}
    		else
    		{
    			LeaveCriticalSection(&g_criticalSection);
    			break;
    		}
    		LeaveCriticalSection(&g_criticalSection);
    	}
    	return 0;
    }


    Visual C++ enthusiast, like network programming and driver development. At present is being engaged in the WinCE/Windows Mobile platform embedded development.

    2012年11月12日 5:20
    版主

全部回复

  • 只有一个线程售票,因为线程 1 的上一个离开和下一个进入挨得太紧,估计轮不上另外一个线程进入。在离开关键段后再调用 Sleep。

    #include <windows.h>
    #include <stdio.h>
    
    int g_nTickets = 100;
    CRITICAL_SECTION g_cs;
    
    DWORD WINAPI Thread(LPVOID p) {
        for (;;) {
            EnterCriticalSection(&g_cs);
    
            if (g_nTickets > 0)
                printf("thread %d %d\n", p, g_nTickets--);
            else {
                LeaveCriticalSection(&g_cs);
                break;
            }
    
            LeaveCriticalSection(&g_cs);
            Sleep(1);
        }
    
        return 0;
    }
    
    int main() {
        HANDLE h1, h2;
    
        InitializeCriticalSection(&g_cs);
        h1 = CreateThread(0, 0, Thread, (LPVOID)1, 0, 0);
        h2 = CreateThread(0, 0, Thread, (LPVOID)2, 0, 0);
    
        WaitForSingleObject(h1, INFINITE);
        WaitForSingleObject(h2, INFINITE);
    
        DeleteCriticalSection(&g_cs);
        CloseHandle(h1);
        CloseHandle(h2);
    }

    退出时有异常,因为你的线程函数从不退出,当 main 中删除了关键段时,两个线程函数再访问关键段就有异常。

    2012年11月11日 20:41
  • InitializeCriticalSection(&g_criticalSection);初始化临界区的代码放到CreateThread创建线程之前做。

    Visual C++ enthusiast, like network programming and driver development. At present is being engaged in the WinCE/Windows Mobile platform embedded development.

    2012年11月12日 1:18
    版主
  • 仅仅将初始化临界区之前还是未能解决问题,谢谢您!

    2012年11月12日 5:13
  • 将我原来的代码根据您说的修正后正常,但您贴出来的代码多了WaitForSingleObject。对我这个初学者来说,这是个新知识,您能跟我说说您这里为什么必须用这个函数?这两个函数在这里是怎么工作的?
    我将WaitForSingleObject(h1, INFINITE);
         
    WaitForSingleObject(h2, INFINITE);


    2012年11月12日 5:16
  • #include <Windows.h>
    #include <iostream>
    #include <tchar.h>
    int fun1Proc();
    DWORD WINAPI Fun1Proc(LPVOID lpParameter);
    int fun2Proc();
    DWORD WINAPI Fun2Proc(LPVOID lpParameter);
    int nticket=100;
    CRITICAL_SECTION g_criticalSection;
    void main()
    {
    	InitializeCriticalSection(&g_criticalSection);
    	HANDLE hThread[2];
    	hThread[0]=CreateThread(0,0,&Fun1Proc,0,0,0);
    	hThread[1]=CreateThread(0,0,&Fun2Proc,0,0,0);
    	
    	WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
    	CloseHandle(hThread[0]);
    	CloseHandle(hThread[1]);
    	DeleteCriticalSection(&g_criticalSection);
    }
    DWORD WINAPI Fun1Proc(LPVOID lpParameter)
    {
    	while(true)
    	{
    		EnterCriticalSection(&g_criticalSection);
    		if(nticket>0)
    		{
    			Sleep(1);
    			std::cout<<"thread 1 sell ticket "<<nticket--<<std::endl;
    		}
    		else
    		{
    			LeaveCriticalSection(&g_criticalSection);
    			break;
    		}
    		LeaveCriticalSection(&g_criticalSection);
    	}
    	return 0;
    }
    DWORD WINAPI Fun2Proc(LPVOID lpParameter)
    {
    	while(true)
    	{
    		EnterCriticalSection(&g_criticalSection);
    		if(nticket>0)
    		{
    			Sleep(1);
    			std::cout<<"thread 2 sell ticket "<<nticket--<<std::endl;
    		}
    		else
    		{
    			LeaveCriticalSection(&g_criticalSection);
    			break;
    		}
    		LeaveCriticalSection(&g_criticalSection);
    	}
    	return 0;
    }


    Visual C++ enthusiast, like network programming and driver development. At present is being engaged in the WinCE/Windows Mobile platform embedded development.

    2012年11月12日 5:20
    版主
  • 将我原来的代码根据您说的修正后正常,但您贴出来的代码多了WaitForSingleObject。对我这个初学者来说,这是个新知识,您能跟我说说您这里为什么必须用这个函数?这两个函数在这里是怎么工作的?
    我将WaitForSingleObject(h1, INFINITE);
    WaitForSingleObject(h2, INFINITE);

    线程函数在执行的时候需要访问关键段,main 在退出时想删除关键段,删除之后就不能再访问了,因此我们必须保证在 main 中删除关键段时,两个线程函数都已经不再访问关键段,否则就异常了。

    确保这一点有很多办法,总的来说需要一个同步信号(同步对象)。main 调用一个函数观察同步信号,只有信号激发才会从观察函数退出,继续执行下面的代码。观察函数就是 WaitForSingleObject,线程本身就是同步对象,创建及执行时是无信号状态,执行 return 退出后变成有信号状态。main 从 WaitForSingleObject 退出,如果等待成功,那说明线程已经结束,可以安全删除关键段。

    2012年11月12日 8:24