运行一下你会发现,这个基本没有什么错误,坐标值对于每个类型基本都是你传入的都是正确的,那么为什么感觉设置的输出位置总是不对呢?只能把目光放在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]);elseprintf("%s",c); LeaveCriticalSection(&cs);} 里面有两个主要函数 EnterCriticalSection(&cs);和LeaveCriticalSection(&cs);第一个可以理解为开前门放一个线程进来然后关前门,第二个是开后门让一个线程出去然后关后门开前门等下一个进程。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-36136-3.html
实弹射击
第一次听说南方黑芝麻糊出问题
美国扰乱了伊拉克
别忘了还有大量的女同志