关于多线程的一个问题

bear234 2016-02-07 07:35:07
加精
我尝试在控制台上模拟“黑客帝国”的字幕雨效果。

这是一个例图:http://www.17sucai.com/pins/3702.html

我是这样设计的:
1)整个场景是由多个字符链组成的,如果控制台的宽度是N,那么这个场景里最多就有N条字符链。
2)每个字符链出现的初始位置和长度都是随机的。
3)字符链是一个class,里面有链头的坐标、链的长度,还有一个show函数用来显示字符链。所以在show函数里,需要调用相关的系统api,这个api可以控制光标的位置。比如初始坐标是(10, 10),长度是4,那么show大概的样子就是(伪码):
for i = 0 to 3
gotoxy(10, 10 - i)
print("随机字母")
end
4)当一个字符链彻底走出控制台后,随机一个时间,比如3秒,等待(sleep)3秒后再重新随机这个字符链的长度和初始位置,然后再调用它的show函数。
5)这里我还用到了多线程,即一个字符链是一个线程,这么一来,如果控制台的宽度是N,那么我就有N个线程在同时运行。




我现在已经按照上面的设计实现出来了,但是效果并不是非常理想,最大的问题就在于:不能同时有太多的字符链。比如我把控制台全屏后,控制台的宽度大概是100多,也就是说我的程序里同时跑了100多个线程,这时候如果不把同时显示的字符链的数量限制一下,比如同时在跑的线程数量不能超过40个(其它线程都处在sleep状态--参见上面的设计4),那么就会带来线程太多而导致不流畅的问题。


我觉得是我的设计本身有问题。
请问我应该如何改进我的程序呢?
...全文
4250 31 打赏 收藏 转发到动态 举报
写回复
用AI写文章
31 条回复
切换为时间正序
请发表友善的回复…
发表回复
lm_whales 2016-07-08
  • 打赏
  • 举报
回复
引用 29 楼 bear234 的回复:
[quote=引用 13 楼 lm_whales 的回复:] 实际上,控制台有滚屏功能,比你自己的一般来说要快
什么是“滚屏”[/quote]就是控制屏幕,上下左右滚动 假设 显示区是 80*25行 但你要显示的是100*40 行 可通过滚屏功能显示任何一行一列
bear234 2016-07-08
  • 打赏
  • 举报
回复
引用 13 楼 lm_whales 的回复:
实际上,控制台有滚屏功能,比你自己的一般来说要快
什么是“滚屏”
bear234 2016-07-08
  • 打赏
  • 举报
回复
引用 16 楼 Rewen 的回复:
我觉得不要开这么多线程了,耗资源的。 先把内容写到memoryDC里面,然后再贴图到DC 就可以了。 通过WM_PAINT消息来刷屏幕,可以做到很炫丽的效果。最多1个线程就够了。
贴图我知道肯定可以 这不是就是想挑战一下么
bear234 2016-07-08
  • 打赏
  • 举报
回复
引用 18 楼 chehw_1 的回复:
把屏幕想像成一个矩阵,定义一个二维数组作为字符输出的缓冲区,把工作线程中的class的show()函数的实现改成写至缓冲区中对应的列。 用一个单独的线程(或主线程)来负责定时在控制台上显示缓冲区中的内容,比如设定每33~100毫秒左右显示(printf)一次。 (如果对显示内容的准确度要求比较高,可能还需对缓冲区的读写操作进行同步)
我觉得你的设计思路不错,但并没有解决我的问题---因为我的问题的核心就是:到底怎样去printf。 比如你说每xx毫秒去printf一次,但怎样printf这个二维数组?从左到右从上到下吗?这样的话依然不会流畅的。 可能我的这个程序没有更好的办法了,因为本身流的输出就比较慢。
bear234 2016-07-08
  • 打赏
  • 举报
回复
引用 30 楼 lm_whales 的回复:
[quote=引用 29 楼 bear234 的回复:] [quote=引用 13 楼 lm_whales 的回复:] 实际上,控制台有滚屏功能,比你自己的一般来说要快
什么是“滚屏”[/quote]就是控制屏幕,上下左右滚动 假设 显示区是 80*25行 但你要显示的是100*40 行 可通过滚屏功能显示任何一行一列[/quote] o o OK
赵4老师 2016-05-19
  • 打赏
  • 举报
回复
引用 25 楼 zhxingway 的回复:
学习一下,好久没来了,上来问问WaitForSingleObject结合信号量使用的问题
《Windows核心编程》
zhxingway 2016-05-18
  • 打赏
  • 举报
回复
学习一下,好久没来了,上来问问WaitForSingleObject结合信号量使用的问题
公交哥的小号 2016-05-17
  • 打赏
  • 举报
回复
围观一下。。。
赵4老师 2016-05-13
  • 打赏
  • 举报
回复
console屏幕处理例子程序。终端窗口屏幕处理相关API使用例子。来自MSVC20\SAMPLES\win32\console\ http://download.csdn.net/detail/zhao4zhong1/3461309
olddai 2016-05-12
  • 打赏
  • 举报
回复
用单线程来控制,一个queue存储一列字符串,queue的入队,出队,访问中间元素这个很方便;屏幕有多宽,定义多少个queue的数组应该能搞定
cxyOOOO 2016-05-11
  • 打赏
  • 举报
回复
// vc控制台,用状态机实现 #include "stdafx.h" #include <conio.h> #include <string> #include <boost/ptr_container/ptr_vector.hpp> #include <boost/statechart/event.hpp> #include <boost/statechart/state_machine.hpp> #include <boost/statechart/state.hpp> #include <boost/statechart/simple_state.hpp> #include <boost/statechart/custom_reaction.hpp> const unsigned int MAX_start_y = 10; class DevOutput { HANDLE _ho; public: DevOutput(): _ho(GetStdHandle(STD_OUTPUT_HANDLE)) { } size_t Width() { return 80; } size_t Height() { return 25; } void CPrint(TCHAR ch, unsigned int x, unsigned int y, unsigned int color) { COORD coor = {static_cast<SHORT>(x), static_cast<SHORT>(y)}; WriteConsoleOutputCharacter(_ho, &ch, 1, coor, NULL); } void FPrint(TCHAR ch, unsigned int x, unsigned int y, unsigned int color, unsigned int fade) { CPrint(_T(' '), x, y, color); } }; #ifdef _UNICODE typedef std::wstring str_t; #else typedef std::string str_t; #endif namespace sc = boost::statechart; struct EvTick: sc::event<EvTick> { }; struct Construct; struct Fall; struct Destruct; struct Fadeout; class SmDrop : public sc::state_machine<SmDrop, Construct> { unsigned int _x; unsigned int _y; unsigned int _start_y; int _interval; unsigned int _fade; DWORD _tick; str_t _str; public: static DevOutput dev; SmDrop(int cln): _x(cln), _y(0), _interval(0), _tick(GetTickCount()) { } bool ReadyPrint() { return int(GetTickCount() - _tick) > _interval; } bool CompletePrint() { return (_y >= dev.Height()) ? true : false; } void Print() { _tick = GetTickCount(); dev.CPrint(_str[_y-_start_y], _x, _y, 0); _y++; } bool ReadyFade() { return ReadyPrint(); } bool CompleteFade() { return _fade == 0; } void Fade() { _tick = GetTickCount(); for (size_t i=_start_y; i<dev.Height(); ++i) dev.FPrint(_str[i-_start_y], _x, i, 0, _fade); if (_fade>0) _fade--; } void Reset(int itv, int fdc) { _tick = GetTickCount(); _start_y = rand() % MAX_start_y; _y = _start_y; _interval = itv; _fade = fdc; _str.clear(); for (size_t i=_start_y; i<SmDrop::dev.Height(); ++i) _str.push_back((_T('a') + rand()%26) & ~(rand()%2 ? _T('\x20') : 0)); } }; DevOutput SmDrop::dev; struct Construct: sc::state<Construct, SmDrop, Fall> { Construct(my_context ctx): my_base(ctx) { context<SmDrop>().Reset(33 + rand()%267, 3); } }; struct Fall: sc::simple_state<Fall, Construct> { typedef sc::custom_reaction<EvTick> reactions; sc::result react(const EvTick& evt) { if (context<SmDrop>().ReadyPrint()) context<SmDrop>().Print(); return context<SmDrop>().CompletePrint() ? transit<Destruct>() : discard_event(); } }; struct Destruct: sc::simple_state<Destruct, SmDrop, Fadeout> { }; struct Fadeout: sc::simple_state<Fadeout, Destruct> { typedef sc::custom_reaction<EvTick> reactions; sc::result react(const EvTick& evt) { if (context<SmDrop>().ReadyFade()) context<SmDrop>().Fade(); return context<SmDrop>().CompleteFade() ? transit< Construct >() : discard_event(); } }; int main() { srand(time(0)); boost::ptr_vector<SmDrop> dms; for (size_t i=0; i<SmDrop::dev.Width(); ++i) { dms.push_back(new SmDrop(i)); dms[i].initiate(); } while (!kbhit()) { for (size_t i=0; i<dms.size(); ++i) dms[i].process_event(EvTick()); Sleep(1); } return 0; }
xiao_angle 2016-05-09
  • 打赏
  • 举报
回复
太厉害了,高手,完全看不懂,唉…
baidu_34927217 2016-05-08
  • 打赏
  • 举报
回复
这个问题非常棒,我看出你是个厉害的人。
chehw_1 2016-05-08
  • 打赏
  • 举报
回复
把屏幕想像成一个矩阵,定义一个二维数组作为字符输出的缓冲区,把工作线程中的class的show()函数的实现改成写至缓冲区中对应的列。 用一个单独的线程(或主线程)来负责定时在控制台上显示缓冲区中的内容,比如设定每33~100毫秒左右显示(printf)一次。 (如果对显示内容的准确度要求比较高,可能还需对缓冲区的读写操作进行同步)
catMiger 2016-05-07
  • 打赏
  • 举报
回复
learning! 博闻新闻
列子汤问 2016-05-07
  • 打赏
  • 举报
回复
我觉得不要开这么多线程了,耗资源的。 先把内容写到memoryDC里面,然后再贴图到DC 就可以了。 通过WM_PAINT消息来刷屏幕,可以做到很炫丽的效果。最多1个线程就够了。
lm_whales 2016-05-06
  • 打赏
  • 举报
回复
实际上,控制台有滚屏功能,比你自己的一般来说要快
lm_whales 2016-05-06
  • 打赏
  • 举报
回复
用for 循环单个输出,已经足够了 不行的话,复制一下就可以了 多线程,就不必了,耽误工夫
qq_34908306 2016-05-06
  • 打赏
  • 举报
回复
我也希望知道代码设计。
赵4老师 2016-05-06
  • 打赏
  • 举报
回复
仅供参考:
// processor: x86
#include <windows.h>
#include <process.h>    /* _beginthread, _endthread */
#include <stddef.h>
#include <stdlib.h>
#include <conio.h>

void Bounce( void *ch );
void CheckKey( void *dummy );

/* GetRandom returns a random integer between min and max. */
#define GetRandom( min, max ) ((rand() % (int)(((max) + 1) - (min))) + (min))

BOOL repeat = TRUE;     /* Global repeat flag and video variable */
HANDLE hStdOut;         /* Handle for console window */
CONSOLE_SCREEN_BUFFER_INFO csbi;    /* Console information structure */

int main()
{
    CHAR    ch = 'A';

    hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );

    /* Get display screen's text row and column information. */
   GetConsoleScreenBufferInfo( hStdOut, &csbi );

    /* Launch CheckKey thread to check for terminating keystroke. */
    _beginthread( CheckKey, 0, NULL );

    /* Loop until CheckKey terminates program. */
    while( repeat )
    {
        /* On first loops, launch character threads. */
        _beginthread( Bounce, 0, (void *) (ch++)  );

        /* Wait one second between loops. */
        Sleep( 1000L );
    }
    return 0;
}

/* CheckKey - Thread to wait for a keystroke, then clear repeat flag. */
void CheckKey( void *dummy )
{
    _getch();
    repeat = 0;    /* _endthread implied */

}

/* Bounce - Thread to create and and control a colored letter that moves
 * around on the screen.
 *
 * Params: ch - the letter to be moved
 */
void Bounce( void *ch )
{
    /* Generate letter and color attribute from thread argument. */
    char    blankcell = 0x20;
    char    blockcell = (char) ch;
    BOOL    first = TRUE;
   COORD   oldcoord, newcoord;
   DWORD   result;


    /* Seed random number generator and get initial location. */
    srand( _threadid );
    newcoord.X = GetRandom( 0, csbi.dwSize.X - 1 );
    newcoord.Y = GetRandom( 0, csbi.dwSize.Y - 1 );
    while( repeat )
    {
        /* Pause between loops. */
        Sleep( 100L );

        /* Blank out our old position on the screen, and draw new letter. */
        if( first )
            first = FALSE;
        else
         WriteConsoleOutputCharacter( hStdOut, &blankcell, 1, oldcoord, &result );
         WriteConsoleOutputCharacter( hStdOut, &blockcell, 1, newcoord, &result );

        /* Increment the coordinate for next placement of the block. */
        oldcoord.X = newcoord.X;
        oldcoord.Y = newcoord.Y;
        newcoord.X += GetRandom( -1, 1 );
        newcoord.Y += GetRandom( -1, 1 );

        /* Correct placement (and beep) if about to go off the screen. */
        if( newcoord.X < 0 )
            newcoord.X = 1;
        else if( newcoord.X == csbi.dwSize.X )
            newcoord.X = csbi.dwSize.X - 2;
        else if( newcoord.Y < 0 )
            newcoord.Y = 1;
        else if( newcoord.Y == csbi.dwSize.Y )
            newcoord.Y = csbi.dwSize.Y - 2;

        /* If not at a screen border, continue, otherwise beep. */
        else
            continue;
        Beep( ((char) ch - 'A') * 1000, 500 );
    }
    /* _endthread given to terminate */
    _endthread();
}


加载更多回复(10)

5,531

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 模式及实现
社区管理员
  • 模式及实现社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧