关于一个程序的“开始——暂停——继续”的一个问题

来源:互联网 发布:android 应用启动优化 编辑:程序博客网 时间:2024/06/06 00:39
楼主发表于: 2011-04-11 21:05:47
    要实现的功能就像360扫描一样,单击“开始”按钮,开始执行,此时按钮的Caption改变为“暂停”,再次单击这个按钮,程序接着上次暂停的地方继续执行。

    在我的程序中,OnBnClicked函数中,由于程序的需要,是创建一个线程,程序功能在线程函数中实现。线程函数主要部分是通过网卡循环向局域网各个主机发送报文并接收它们的响应报文进行相应的处理,其中又用到很多自定义的函数模块。

C/C++ code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void CGetMACDlg::OnBnClickedScanmac()
{
          /*................
          ..................
          ..................这些为打开本机网卡部分(为后面的发送——接收报文做准备)
          ..................
          */
          HWND h_wnd = ::GetDlgItem(this->m_hWnd,IDC_SCANMAC); 
    char* WinTitle=new char();
    int len = ::GetWindowTextLengthA(h_wnd);
    int x = ::GetWindowTextA( h_wnd,WinTitle,len+1); 
    CString str ="";
    str.Format("%s",WinTitle);
    CString str1 = "扫描";
    CString str2 = "暂停";
 
    if(str.Compare(str1))
    {
        ScanEthernet();//创建线程函数(CreateThread)
        ::SetWindowText(h_wnd,"暂停");
    }
    if(str.Compare(str2))
    {
                  。。。。。。。。。。。。//暂停线程(目前还不知如何写)
                  ::SetWindowText(h_wnd,"扫描");
    }
 
}


没有思绪了,貌似要用到信号量:
申请一个信号量:

hEvent=::CreateEvent(NULL,TURE,TURE,lpName);//第二个参数是TURE,设定为手动模式,防止::WaitForSingleObject改变信号量的状态。


在所有线程可以被暂停的地方加上:


::WaitForSingleObject(hEvent,-1);


这样,只需要用这两个函数


::SetEvent(hEvent);//继续
::ResetEvent(hEvent);//暂停

疑问一:如果用信号量的话,我这个程序改怎么改?“暂停”、“继续”函数具体加在什么地方?(如果这么用,我觉得线程中的循环可能会出问题,欢迎大家讨论一下)
疑问二:用SuspendThread  挂起(暂停)线程、ResumeThread  继续执行线程函数是否会好一些?
疑问三:程序逻辑上肯定存在问题,程序刚启动按钮自然是“扫描”状态,第一次单击逻辑没有问题,创建线程执行;第二次单击,暂停线程,逻辑也没有问题,当第三次单击时,逻辑就有问题了:应该是继续执行线程,而不是再次创建线程。
改成这样:
C/C++ code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void CGetMACDlg::OnBnClickedScanmac()
{
          /*................
          ..................
          ..................这些为打开本机网卡部分(为后面的发送——接收报文做准备)
          ..................
          */
          HWND h_wnd = ::GetDlgItem(this->m_hWnd,IDC_SCANMAC); 
    char* WinTitle=new char();
    int len = ::GetWindowTextLengthA(h_wnd);
    int x = ::GetWindowTextA( h_wnd,WinTitle,len+1); 
    CString str ="";
    str.Format("%s",WinTitle);
    CString str1 = "扫描";
    CString str2 = "暂停";
 
         ScanEthernet();//创建线程函数(CreateThread)
    ::SetWindowText(h_wnd,"暂停");
 
    if(str.Compare(str2))
    {
                  。。。。。。。。。。。。//暂停线程(目前还不知如何写)
                  ::SetWindowText(h_wnd,"扫描");
    }
    if(str.Compare(str1))
    {
                  。。。。。。。。。。。。//继续线程(目前还不知如何写)
                  ::SetWindowText(h_wnd,"暂停");
    }
 
}

好像逻辑也不对!!!

初次接触这个东西,望大家指教!!


  •  
  •  
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
回复次数:30
#1 得分:5回复于: 2011-04-11 21:07:37
顶一下,你比我强多了,我不知道呵呵、
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#2 得分:5回复于: 2011-04-11 21:18:17
开始、暂停、继续 用一个变量就行了

对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#4 得分:5回复于: 2011-04-11 21:20:40
SuspendThread(m_hHandle); 
m_hHandle是你Create的线程的句柄
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#5 得分:5回复于: 2011-04-11 21:23:29
创建一个BOOL变量表明是否需要创建线程,如果第一次当然创建,之后点击就不用创建了,不知道对不对哦
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#6 得分:10回复于: 2011-04-11 21:24:49
C/C++ code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int run = 0;
int step = 0;
 
on_click_run
{
    run = !run;
    click_start();
}
 
click_start()
{
    do
    {
        if( !run )
        {
            return;
        }
        else
        {
            do_some_thing( step );
            step++;
        }
    while( 1 );
}
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#7 得分:30回复于: 2011-04-11 21:25:37
疑问一: CreateSemphore/ReleaseSemaphore,和EVENT的处理差不多的,SetEvent/ReSetEvent()

疑问二:不建议使用SuspendThread/ResumeThread();因为SuspendThread和ResumeThread的作用其实很简单,他们都是通过线程的HANDLE到线程的数据结构中将一个计数的变量做加一减一的操作, 如果线程在运行时监测到线程的Suspend Count计数大于0,那么线程就会暂停处理任何消息和循环的进行,这时CPU将不为该线程分配时间片,如果线程的Suspend Count小于等于0,线程将正常运行。所以ResumeThread的一次调用并不一定导致线程的运行,SuspendThread也不一定导致线程挂起,关键在于线程的挂起计数。

疑问三:判断一下是否存在
C/C++ code
?
1
2
3
4
5
6
static BOOL bScan = FALSE;
::SetWindowText(h_wnd, (bScan = !bScan) ?  _T("扫描") : _T("暂停"));
if(bScan)
{
  // 创建线程
}
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#8 得分:5回复于: 2011-04-11 21:27:12
DWORD WINAPI SuspendThread(
  __in  HANDLE hThread
); // 暂停

DWORD WINAPI ResumeThread(
  __in  HANDLE hThread
);// 重新开始
http://msdn.microsoft.com/en-us/library/ms682453(v=vs.85).aspx
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#9 得分:5回复于: 2011-04-11 21:31:40
创建个BOOL型的变量用来判断
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#10 得分:20回复于: 2011-04-11 21:32:51
程序"开始"时创建线程,你需要执行的操作放到线程中的while循环中,循环中每次判断WaitForSingleObject(_hEvent, INFINITE);

如果需要"继续",SetEvent(_hEvent);
需要"暂停",ResetEvent(_hEvent);
看看这个程序,不知道让你对Event的用法有些启发:
C/C++ code
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <windows.h>
#include <iostream>
using namespace std;
 
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
 
int tickets=100;
HANDLE g_hEvent;
 
void main()
{
HANDLE hThread1;
HANDLE hThread2;
 
//**************************************************
//创建一个命名的自动重置事件内核对象
 
g_hEvent=CreateEvent(NULL,TRUE,FALSE,LPCTSTR("tickets"));
  
if (g_hEvent)
{
if (ERROR_ALREADY_EXISTS==GetLastError())
{
cout<<"only one thread can run!"<<endl;
return;
}
}
 
//**************************************************
SetEvent(g_hEvent);
 
//创建线程
hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
 
Sleep(40000);
//关闭事件对象句柄
CloseHandle(g_hEvent);
};
 
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while (TRUE)
{
WaitForSingleObject(g_hEvent, INFINITE);
 
BOOL bReSet = ResetEvent(g_hEvent);
 
if (tickets>0)
{
cout<<"thread 1 sell ticket :"<<tickets--<<endl;
}
else
{
break;
}
SetEvent(g_hEvent);
Sleep(10);
}
 
return 0;
};
 
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while (TRUE)
{
WaitForSingleObject(g_hEvent, INFINITE);
 
BOOL bReSet = ResetEvent(g_hEvent);
 
if (tickets>0)
{
Sleep(1);
cout<<"thread 2 sell ticket :"<<tickets--<<endl;
}
else
{
break;
}
SetEvent(g_hEvent);
Sleep(10);
}
 
return 0;  
};
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#11 得分:5回复于: 2011-04-11 21:38:11
使用PV操作
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#12 得分:5回复于: 2011-04-11 22:00:35
hEvent=::CreateEvent(NULL,TURE,TURE,lpName);//第二个参数是TURE,设定为手动模式,防止::WaitForSingleObject改变信号量的状态。


在所有线程可以被暂停的地方加上:


::WaitForSingleObject(hEvent,-1);


这样,只需要用这两个函数


::SetEvent(hEvent);//继续
::ResetEvent(hEvent);//暂停

对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#13 得分:5回复于: 2011-04-11 23:24:57
路过的,看下
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#14 得分:5回复于: 2011-04-11 23:58:24
线程应该不退出,只是判断那个全局变量,如果处于暂停,那么就忽略对应的执行代码吧。
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#15 得分:20回复于: 2011-04-12 00:25:38
没有多难吧

code=C/C++]

//设置两个全局变量或CGetMACDlg成员函数标记
   bool juststart=0;//
   bool nowstate=0;
void CGetMACDlg::OnBnClickedScanmac()
{
          /*................
          ..................
          ..................这些为打开本机网卡部分(为后面的发送——接收报文做准备)
          ..................
          */
   
    HWND h_wnd = ::GetDlgItem(this->m_hWnd,IDC_SCANMAC); 
    char* WinTitle=new char();
    int len = ::GetWindowTextLengthA(h_wnd);
    int x = ::GetWindowTextA( h_wnd,WinTitle,len+1);        

    if(nowstate==0)
    {
if(juststart==0){//刚启动
ScanEthernet();//创建线程函数(CreateThread)
juststart=1;//已经启动
nowstate=1;
::SetEvent(hEvent);//执行线程
::SetWindowText(h_wnd,"暂停")
nowstate=1;

}else{
//继续线程
::SetEvent(hEvent);//继续
nowstate=1;
::SetWindowText(h_wnd,"暂停")

}     
                  
    }else
{   //暂停线程

         ::ResetEvent(hEvent);//暂停
 nowstate=0;
         ::SetWindowText(h_wnd,"扫描");;
    }

}

[/code]
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#16 得分:5回复于: 2011-04-12 00:49:49
无论你是使用临界区对象、互锁函数、事件内核对象、信号量内核对象还是互斥内核对象,原理都差不多,也很简单

至于点第三次就出现错误,我猜想是创建线程的缘故吧,点第三次的时候你又创建了第二个线程额
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#17 得分:5回复于: 2011-04-12 01:00:04
在循环的函数里是否继续处理下一个的地方加上对信号量的判断不就可以了?

一般的应用,Event实现的接口能算绰绰有余了。
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#18 得分:5回复于: 2011-04-12 01:18:14
我刚在没看内容瞎说话:

我觉得你的需要,这样实现的话应该可以做到比较简单的管理
每个线程的参数都作为传递它要扫描对象的参数。
线程在开始处理这堆对象里的一个之前的当前处理数量的判断里,也判断下自己对应的信号,从二个原因判断出来是不是允许它执行下去,当然在判断信号的地方可以用Wait的方式等待就更好。不然其他的暂停处理也可以在判断循环的这儿进行暂停。
在主线程或者单独一个线程的里面,维护一份线程使用的结构列表,比方结构里就保存着线程所处理的目标们,控制线程用的句柄,还有它所申请的内存片等需要释放的数据也放在里面,当然还有决定它暂停与执行的变量。
这样在主线程里,需要处理某个目标时,直接在这个列表里找有没有对应的线程,没就创建,有就设置执行的变量。需要暂停就寻找有没有对应的线程,有就设置暂停的变量。
当结束处理某个目标,或者就在换目标进行处理的时候想结束掉对其他目标的处理,那就在监视线程结构列表的流程里做一个释放线程用数据的函数,直接释放了再Kill那个Thread就好了。
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#20 得分:5回复于: 2011-04-12 07:00:29
思路:
     1)建立通信模块Ccomm ,该摸块有堵塞/通信功能,相信从现有类里不难实现.
     2)建立通信块的序列Ccomm m_cmm[25];
     3)只用一个线程,该线程扫描序列m_cmm.用静态变亮记述当前序列位置
      4)在Ccomm中记录通信状态
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#21 得分:5回复于: 2011-04-12 08:15:29
都是自己定义的逻辑控制,记录当前状态就可以了,知道当前的什么状态就好操作了
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#22 得分:4回复于: 2011-04-12 09:16:29
设置一个BOOL变量,为真给线程发操作事件,为假不发,用timer时钟来发 

就可以实现运行  暂停  继续啦 
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#23 得分:30回复于: 2011-04-12 09:51:45
小兄弟
我给你做了一个小例子
不敢说实现有多么合理
不过应该会给你些启示
有兴趣的话留个邮箱
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#24 得分:0回复于: 2011-04-12 11:14:23
引用 23 楼 xianglitian 的回复:
小兄弟
我给你做了一个小例子
不敢说实现有多么合理
不过应该会给你些启示
有兴趣的话留个邮箱

感激不尽!!!!
有劳大哥发一下:
569051911@qq.com
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#25 得分:0回复于: 2011-04-12 11:17:10
顺便问一下:
WaitForSingleObject(xxxx,INFINITE)函数在我的这个程序当中,应该放在线程处理函数当中的 数据包发送与接收这个循环 的前面吧?
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#26 得分:0回复于: 2011-04-12 11:42:12
我发了
你查收
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#27 得分:5回复于: 2011-04-12 11:45:05
引用 25 楼 lsq19871207 的回复:
顺便问一下:
WaitForSingleObject(xxxx,INFINITE)函数在我的这个程序当中,应该放在线程处理函数当中的 数据包发送与接收这个循环 的前面吧?

放在你的业务操作开始前
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#28 得分:3回复于: 2011-04-12 11:46:12
我是新手说下我的方法,我做的是批量文件转换,定义BOOL类型的变量 m_bRun,初始化index为0,index为文件个数,这样控制m_bRun的值可以实现暂停和继续功能
for (;index<m_list.GetItemCount();index++)
{
try
{
转换函数。。 }
catch(_com_error e)
{
CString errorMessage=e.ErrorMessage();
AfxMessageBox(_T("出错")+errorMessage);//显示错误信息
}
if (!m_bRun)//暂停
{
index++;
break;
}

}
if (index==m_list.GetItemCount())
{
MessageBox(_T("全部完成!"));
}

if(index>=m_list.GetItemCount())
{
index = 0;

}
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#29 得分:3回复于: 2011-04-12 12:08:39
Event,很好. 我就用Event写了个线程池. 
对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理
#30 得分:0回复于: 2011-04-12 14:06:54
万事开头难,经过大家的指导,这个功能已经实现,先谢谢大家!

现总结一下:

创建线程函数ScanEthernet
1、在线程创建的时候申请信号量,如我的程序:
C/C++ code
?
1
2
3
4
5
6
7
void CGetMACDlg::ScanEthernet()
{   
    g_hEvent=::CreateEvent(NULL,TRUE,FALSE,NULL);
    HANDLE hThread=CreateThread(NULL,0,PacketRec,(LPVOID)this,0,NULL);
    CloseHandle(hThread);
     
}



2、根据程序的需要,在线程处理函数中添加WaitForSingleObject(g_hEvent, INFINITE)函数,在我的程序中,是在for循环中开始位置添加的 ,因为这个发送数据包循环是线程处理函数的主体部分,程序运行起来后除了初始化,就是通过这个循环来实现其功能,因此,加在循环外自然是不合理的
3、在单击按钮处理函数中,根据自身程序的需要,添加
::ResetEvent(g_hEvent);//暂停 
和 
::SetEvent(g_hEvent);//继续

注意:

CreateEvent(NULL,TRUE,FALSE,NULL)函数的第二个参数一定要设置为TRUE,即手动模式,防止::WaitForSingleObject改变信号量的状态,一开始我就把其设置为FALSE,结果是单击按钮时,只循环执行一次,而且会出现“继续”与“暂停”执行效果相反的情况,这就是WaitForSingleObject本身改变信号量的状态 所造成的
阅读全文
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 快手绑定的手机号丢了怎么办 球球大作战绑定手机号丢了怎么办 快手绑定手机号丢了怎么办 绑定银行卡的手机号丢了怎么办 爱奇艺绑定的手机号丢了怎么办 高铁票订错时间怎么办 微信音频视频打出没反应怎么办 海尔超低温保存箱低温报警怎么办 孕期吃了烧烤蔬菜怎么办 百姓动迁政府说话不算数怎么办 孕妇用了青草膏怎么办 药材不知道啥名怎么办 诛仙手游鸿蒙紫气不见了怎么办 紫叶兰叶干了怎么办 绿萝叶子中毒了怎么办? 怀孕初期胃疼的厉害怎么办 怀孕胃疼的厉害怎么办 天然气热水器风口一封闭怎么办 热水器着火后火变大怎么办? 人如果没有天魂怎么办 被鬼魂附体了该怎么办 被吸血蠓咬了怎么办 股份公司大股东占用资金怎么办 抱小孩抱的胳膊疼怎么办 一个月好几个性伴侣怎么办 惹了势力大的人怎么办 给佛像换新法器旧的怎么办 cad填充图案拾取不了点怎么办 cad填充图案太密怎么办 桃木葫芦挂件裂口了怎么办 皮肤被太阳晒伤变黑怎么办 皮肤又粗又黑该怎么办 盆栽红薯长疯了怎么办 四叶草叶子发黄怎么办 美篇不想让别人看到怎么办 黑魂3暗灵入侵怎么办 问道手游账号忘了怎么办 问道手游密码忘了怎么办 毒蚂蚁咬了红肿怎么办 电脑开机后有电流声怎么办 小学生阅读方面不会总结怎么办