首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

如何能让线程不阻塞窗口按钮的点击

2012-08-14 
怎么能让线程不阻塞窗口按钮的点击小弟新写了一线程的程序,现在想在程序中点击按钮后触发线程函数,比如我

怎么能让线程不阻塞窗口按钮的点击

小弟新写了一线程的程序,现在想在程序中点击按钮后触发线程函数,比如我点一次按钮现在开始执行我线程里的内容,当线程里的内容在执行中时,我再点这按钮,会再起一线程,这线程也还是以下这函数,我还想再点一次按钮时我的窗口停止我的鼠标点击这按钮了,我想知道怎么才能让一个按钮能排队三次线程的操作并在操作后消毁掉这些线程,小弟的程序有些阻塞,请各位大大帮忙看看。


C/C++ code
void CMainFrameWnd::DownloadFilesToDisk(){//    MessageBoxA(NULL,"等待中",NULL,NULL);    DWORD    dwRet = GlobalmplFun::WaitThread(m_Thread);    m_Thread =::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ClasicThreadProc, NULL, 0, &threadID);}


C/C++ code
DWORD GlobalmplFun::WaitThread( HANDLE waitThread,DWORD waitTimer/*=INFINITE*/,BOOL bInput/*=TRUE*/ ){    MSG msg;    DWORD ret;    if(!waitThread)        return 0;    while(waitThread)    {        ret=MsgWaitForMultipleObjects(1,&waitThread,FALSE,waitTimer,QS_ALLINPUT);//        if(ret==WAIT_OBJECT_0+1)        {            while(PeekMessage(&msg,NULL,0,0,PM_REMOVE))            {                if(msg.message==WM_QUIT)                {                    CloseThread(waitThread);                    DispatchMessage(&msg);                    return 0;                }                if(bInput)                {                    if((msg.message<WM_MOUSEFIRST||msg.message>WM_MBUTTONDBLCLK)&&(msg.message<WM_KEYFIRST||msg.message>WM_KEYLAST))                        DispatchMessage(&msg);                }                else                {                    DispatchMessage(&msg);                }            }        }        else if(ret==WAIT_OBJECT_0)        {            return CloseThread(waitThread);        }    }    return 0;}



[解决办法]
DWORD dwRet = GlobalmplFun::WaitThread(m_Thread);
在这个里你调用了ret=MsgWaitForMultipleObjects(1,&waitThread,FALSE,waitTimer,QS_ALLINPUT);//
当然后有一段时间的等待时长,如果你只要多线程的话,完全没必要写这个。
只要ClasicThreadProc保证能在不是超常时间内安全退出就行,
你完全可以在添加一个全局的数组或者vector<int>或map<int threadID , int threadState>来标记线程状态,把当前wnd的
this指针传入当成线程参数,或结构体指针的一部分传入新的线程,在线程return
前用this指针更改当前线程状态。这样就不会有阻塞的情况,并且你能通过数组状态知道当前线程状态。
对更改当前线程状态的代码放入临界区或者加锁!或者设置标志位进行操作,防止数据状态出现问题。

[解决办法]
不要每次DownloadFilesToDisk()时都调用
DWORD dwRet = GlobalmplFun::WaitThread(m_Thread);
并且将::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ClasicThreadProc, NULL, 0, &threadID);
返回的HANDLE存储在数组里,在要结束的时候统一WaitThread并CloseHandle
[解决办法]
你为什么不做成点击一次按钮然后让按钮变灰,只执行一次线程操作呢,这样给用户的体验也好一点。。。
[解决办法]
完美的解决方案就要考虑周全, 不能留BUG, 东猜西猜乱蒙是写不了好程序的.

必须启一个专门的任务管理线程始终不退出, 它负责定时轮询检测所有已创建的工作线程, 控制并发数目, 回收已完成线程, 最终要的一点在于: 它将通过一个任务队列与按钮点击事件进行交互, 按钮点击一次则向队列push一个任务, 由任务管理线程pop任务创建线程.

这样不会丢任务, 也不会堆积任务, 线程管理也变的可控了.

任务管理线程的基本结构如下:

1, 加锁1, 遍历已创建的工作线程列表, 回收已完成的线程, 然后放掉锁1.
2, 根据1中获得的当前线程数, 不超过3个则允许创建新线程
3, 允许创建新线程, 则加锁2, 检测任务队列, 有task就pop出来创建线程, 放掉锁2.
4, 根据task创建新线程,记录到工作线程列表中,线程计数也+1.

这个过程是轮询的,适当的睡眠,挂起是必要的,不能睡在线程wait上,只能睡在任务队列上,睡的条件是:
线程数没有到达3个,而且任务队列为空。因为这样才能保证一旦被唤醒就可以pop一个task立即创建线程,否则就应该尽快的回收线程让出位置来, 而不应该挂在任务队列上等下去.

热点排行