设为首页 加入收藏

TOP

windows多线程编程星球(一)(三)
2017-10-11 18:24:43 】 浏览:740
Tags:windows 线程 编程 星球
sp;      首先,分析一下为何会出现这样的行为,我们的函数不多,可以用注释一部分的办法来试试看看能不能找到问题在哪里。第一步,首先把createthread注释掉,或者倒数第二个参数使用CREATE_SUSPENDED,这样线程会被挂起,并不会被执行。你会发现,在只有一个主线程的情况下,一切运行如你所料,说明创建一个线程确实会导致不友好的显示问题。按照这个思路,接下来我们应该到线程函数threadproc里面找一找原因,一共也就俩函数,想到是显示出了问题,所以最大的怀疑对象是在函数MoveOutputToPos上面,既然这样,我们把move和spin里面MoveOutputToPos函数注释掉再试试,发现也没有问题。那么可以把注意点放在这个函数里面了。这个函数主要由两个部分组成,SetConsoleCursorPosition和printf,另外还有一个给CONSOLE_SCREEN_BUFFER_INFO变量赋值。为了一探究竟到底发生了啥,我们是稍微临时改造一下这个函数,传入星球的类型,这样我们就可以做一些输出了。第一步,现在变量CONSOLE_SCREEN_BUFFER_INFO赋值后面加入printf(“%d->x:%d,y:%d\r\n”,type,csbiInfo.dwCursorPosition.X ,csbiInfo.dwCursorPosition.Y)。运行一下你会发现,这个基本没有什么错误,坐标值对于每个类型基本都是你传入的都是正确的,那么为什么感觉设置的输出位置总是不对呢?只能把目光放在SetConsoleCursorPosition另外一个参数handle hout上面了,这个参数是是一个全局变量,那么从代码的角度出发也就是说作用域是整个代码文件,也就是说不管是main还是threadproc都是使用的这一个值,而在调用SetConsoleCursorPosition时,就可能发生主线程调用的这个函数但是下一秒切换成子线程的hout拷贝到寄存器上,造成了混乱。这就是多线程编程中的一个永恒而又核心的问题,资源的竞争。

        资源,在任何世界里都是核心问题,无论是现实还是虚拟,一个世界的运行最本质的就是建立在资源的基础上。所有的战争本质上就是资源分配的不合理,无法满足自我的需求或者觊觎别人的资源。在计算机多线程的世界里,资源的问题严重的会导致程序崩溃,轻一点的会导致程序不当运行,虽然虚拟内存保证了资源是无法绕过进程的底线但是这也是无法容忍的。在我们目前的程序里面,有两个线程,主线程和子线程都能访问到这个进程的资源,也就是这个console windows。而计算机在运行多线程的时候实际上是以很快的速度不停的切换当前运行的进程,这样每个线程都可以分配到一点CPU的时间从而运行自己。为什么作为终端用户我们感觉不出来,是因为这个切换速度十分的快并且十分的频繁,导致我们根本感觉不到,所以给你一个假象是两个线程在并行(同时)的运行。这种切换是一个复杂的过程,但是可以推理的到就是他们会使用一些公用的空间,因为毕竟只有一个进程。在我们的这个例子里面,主线程和子线程在运行的时候都想在这个dos窗口上输出,所以他们会不停的试图抢占这个dos窗口,但是他们的输出坐标是各自的。所以会导致一个问题,在主线程计算出自己坐标之后突然发生了切换,这时候子线程取得dos窗口的控制权,于是本来该画在下面子星球位置上的符号画在了一个看起来很奇怪的位置上。

        要解决这个问题的在多线程编程中有很多,而且后面遇到的时候也很多,我准备每次用一个不同的办法,在这里先介绍一个最简单的,CRITICAL_SECTION。CRITICAL_SECTION从名字中就能看出其霸气的作用,关键区域,读多了有种紧迫感。CRITICAL_SECTION其实简单的我觉得他就像一个看门人,在它锁定的区域它一次只放进来一个线程,如果屋里有人了,就不在允许第二个线程进入了。在本程序中,首先你得声明一个全局的CRITICAL_SECTION cs;然后得在main函数的最开始调用InitializeCriticalSection(&cs);来初始化,然后在离开main的时候得使用DeleteCriticalSection(&cs);来销毁这个对象。然后就是要更新一下MoveOutputToPos函数:

void MoveOutputToPos(int x,int y,char* c,bool bString)
{     
   
    CONSOLE_SCREEN_BUFFER_INFO csbiInfo; 
    csbiInfo.dwCursorPosition.X = x;
    csbiInfo.dwCursorPosition.Y = y; 

    EnterCriticalSection(&cs);
    if (!SetConsoleCursorPosition(hOut,csbiInfo.dwCursorPosition)) 
    {
        printf("SetConsoleCursorPosition error!!!!! \r\n"); 
        LeaveCriticalSection(&cs);
        return;
    }

    if(!bString)
        printf("%c",c[0]); 
    else
        printf("%s",c);
    LeaveCriticalSection(&cs);
}

       里面有两个主要函数 EnterCriticalSection(&cs);和LeaveCriticalSection(&cs);第一个可以理解为开前门放一个线程进来然后关前门,第二个是开后门让一个线程出去然后关后门开前门等下一个进程。所以这两个函数要成对出现,无论你调用多少次,但是一定要配对,不然就出现前门关了再也没开过导致其他线程进不来或者前门一直大开没有任何效果。如此运行一下你会发现一切变得如你所料的显示了,因为在这里我们把主要之前的竞争资源hout做了管理。

      【更多】本来感觉这一节感觉我应该深入的说明一下CRITICAL_SECTION以及各种相关函数的原理,但是我觉得我怎么写也不可能比这一篇更通俗易懂了:http://my.oschina.net/myspaceNUAA/blog/81244,所以我决定我稍微扯一下windows的进程。首先要明确的一点是进程并不会执行任何代码,操作系统调度和执行的最小单位是线程,而进程都至少有一个线程。那么进程到底有什么用呢?进程给线程的执行提供了环境,他是线程执行的容器,可以保证线程一直在每个进程空间中执行。如果两个线程在同一个进程中执行,那么这两个线程可以共享进程的地址空间,他们可以共享进程里面的代

首页 上一页 1 2 3 4 5 6 下一页 尾页 3/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇win7如何恢复以前的ie版本 下一篇在IE中解决当前安全设置不允许下..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目