SDL入门教程(十三):2、初识多线程

作者:龙飞

2.1:竞争条件(Race Conditions)

        我们在前面将一个普通函数调用转换成了用线程调用,这意味着我们可以“同时”调用两个以上的线程。例如,我们希望在屏幕的另外一个位置也播放这段简单的动画,我们只需要添加一个线程的调用就可以了。

int  main( int  argc , char *  argv[])
{
    
// Create a SDL screen.
     const   int  SCREEN_WIDTH  =   640 ;
    
const   int  SCREEN_HEIGHT  =   480 ;
    
const  Uint32 SCREEN_FLAGS  =   0 // SDL_FULLSCREEN | SDL_DOUBLEBUF | SDL_HWSURFACE
     const  std:: string  WINDOW_NAME  =   " Amn Test " ;
    ScreenSurface screen(SCREEN_WIDTH, SCREEN_HEIGHT, WINDOW_NAME, 
0 , SCREEN_FLAGS);

    PictureSurface bg(
" ./images/background.png " , screen);
    bg.blit(
0 );
    screen.flip();

    AmnArg test1(
0 250 600 250 , screen);
    SDL_Thread
*  thread1  =  SDL_CreateThread(amn, ( void * ) & test1);

    AmnArg test2(
0 0 600 0 , screen);
    SDL_Thread
*  thread2  =  SDL_CreateThread(amn, ( void * ) & test2);

    SDL_Event gameEvent;
    
bool  gameOver  =   false ;
    
while  ( gameOver  ==   false  ){
        
while  ( SDL_PollEvent( & gameEvent)  !=   0  ){
            
if  ( gameEvent.type  ==  SDL_QUIT ){
                gameOver 
=   true ;
            }
            
if  ( gameEvent.type  ==  SDL_KEYDOWN ){
                
if  ( gameEvent.key.keysym.sym  ==  SDLK_ESCAPE ){
                    gameOver 
=   true ;
                }
            }
            screen.flip();
        }
    }

    SDL_KillThread(thread1);
    SDL_KillThread(thread2);
    
return   0 ;
}

这段程序看起来似乎没有什么问题,但是运行的时候,不可预知的情况出现了:理论上我们几乎同时调用了两个线程,动画似乎应该是同步播放的,但是实际上,两段动画的播放并不同步,并且每次执行的效果都不一样——有时候上面的图片移动快,有时候下面的图片移动快,并且速度不均匀。
        这就是典型的race conditions的表现。还记得我说过没有定义dt吗,我们让电脑以其所能达到的最快速度决定dt,换句话说,我们每一个线程都试图“咬死”CPU的运算,当然,在实际中多任务的OS会帮助CPU分配任务,但是如何分配却是不确定的,因为OS并不知道哪些任务需要优先执行,所以,两个线程实际上在竞争电脑的性能资源,产生的结果就是不确定的。

2.2:松开“死咬”的CPU

void  SDL_Delay(Uint32 ms);

        解决race conditions的方法就是给CPU足够的时间“休息”,而这正好也是我们自己定义dt所需要的。SDL_Delay()在这个时候就显得意义重大了。当今电脑的运算速度非常非常快,以至于哪怕我们仅仅给电脑0.01秒的时间“休息”(每次循环中),电脑都会显得很轻松了。

int  amn( void *  data)
{
    AmnArg
*  pData  =  (AmnArg * )data;
    PictureSurface stand(
" ./images/am01.png " , pData -> screen);
    stand.colorKey();
    PictureSurface bg(
" ./images/background.png " , pData -> screen);

    
const   int  SPEED_CTRL  =   300 ;
    
int  speedX  =  (pData -> endX  -  pData -> beginX)  /  SPEED_CTRL;
    
int  speedY  =  (pData -> endY  -  pData -> beginY)  /  SPEED_CTRL;

    
for  (  int  i  =   0 ; i  <  SPEED_CTRL; i ++  ){
        pData
-> beginX  +=  speedX;
        pData
-> beginY  +=  speedY;
        bg.blit(pData
-> beginX, pData -> beginY, pData -> beginX, pData -> beginY, stand.point() -> w, stand.point() -> h,  2 2 );
        stand.blit(pData
-> beginX, pData -> beginY);
        pData
-> screen.flip();
        SDL_Delay(
10 );
    }

    
return   0 ;
}

说到这里,我们不得不提及之前一直所忽略的一个问题:我们之前凡是涉及循环等待事件轮询的程序总是占用100%的CPU,这并不是因为我们真正用到了100%的CPU性能,而是我们让CPU陷入了“空等”(Busy Waiting)的尴尬境地。轮询事件得到响应相对于循环等待来说,是发生得非常缓慢的事情,我们在循环中,哪怕是让电脑休息0.01秒,事情都会发生本质性的改变:

     while  ( gameOver  ==   false  ){
        
while  ( SDL_PollEvent( & gameEvent)  !=   0  ){
            
if  ( gameEvent.type  ==  SDL_QUIT ){
                gameOver 
=   true ;
            }
            
if  ( gameEvent.type  ==  SDL_KEYDOWN ){
                
if  ( gameEvent.key.keysym.sym  ==  SDLK_ESCAPE ){
                    gameOver 
=   true ;
                }
            }
            screen.flip();
        }
        SDL_Delay(
10 );
    }

当我们重新运行新程序的时候,我们可以看到程序对CPU的占用从100%骤降到了0%!这当然并不意味着程序就用不上CPU了,而是说,这些运算对于我们的CPU来说,实在是小菜一碟了,或者从数据上说,处理这些运算的时间与0.01秒来比较,都几乎可以忽略不计!

2.3:GUI线程与worker线程

        我们的另外一项试验是将事件轮询放到动画线程中,程序就不多写了,大家可以自己试下。我直接说结论:动画线程中无法响应事件轮询。
        一般提倡的模式,是将GUI事件都写在主线程中,而将纯粹的运算才写到由主线程创建的线程中,后者也就是所谓的worker线程。从另外一个概念看,只有主线程控制着“当前窗口”,其它线程也许在后台,也许虽然也是在前台但是并非是我们可见的,所以,轮询事件找不到接口。
        对于抛出的线程与主线程之间的通讯,我们可以通过他们共享的数据来进行控制,所以,尽管事件轮询不能直接影响worker线程,但是我们仍然是可以通过主线程进行间接影响的。

 

此文章来自于【http://blog.csdn.net/lf426/article/details/2657235

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值