VerySource

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1327|回复: 10

我想了几天的问题,请各位兄弟来看看,急我所难!!线程传参

[复制链接]

1

主题

7

帖子

5.00

积分

新手上路

Rank: 1

积分
5.00
发表于 2020-9-25 11:30:02 | 显示全部楼层 |阅读模式
这个例子是 VC++ 高级编程技术与实例  第五章多线程的一个例子
1:
建立一个基于对话框的程序Test
2:
在对话框上放置一个按钮IDC_BT_START.和一个进度条控件IDC_PROGRESS1
并为IDC_PROGRESS1关联一个变量m_progress;
3:
在TestDlg.h文件中,定义一个结构体
struct threadInfo
{
        CProgressCtrl * p_Progress;
        CDialog * p_Dlg;  
        int  second;     //进度条暂停时

}
4:
在TestDlg.cpp文件中
#define USER_PROC_FINISHED WM_USER+1

threadInfo Info;
UINT ThreadProc(LPVOID pParam)
{
        threadInfo * p=(threadInfo *)pParam;
        for(int i=0;i<=100;i++)
        {
                p->p_Progress->SetPos(i);
                ::Sleep(p->second*10);


        }
        ::PostMessage(p->p_Dlg->GetSafeHwnd(),USER_PROC_FINISHED,0,0);
        return 0;
}
5:
在按钮IDC_BT_START的单击响应中:
void CTestDlg::OnBtStart()
{
       
        Info.p_Progress=&m_progress;
        Info.p_Dlg=this;
             Info.second=1;
        AfxBeginThread(ThreadProc,&Info);
}
6:
在TestDlg.h中声明消息函数
       
      .........
    //}}AFX_MSG
    afx_msg void OnFinish();
7.
在TestDlg.cpp文件中
BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
         .......
        ON_BN_CLICKED(IDC_BT_START, OnBtStart)
        //}}AFX_MSG_MAP
        ON_MESSAGE(USER_PROC_FINISHED,OnFinish)
END_MESSAGE_MAP()

void CTestDlg::OnFinish()
{
        SetDlgItemText(IDC_BT_START,"线程结束请重新启动");
}




在候捷译的<Win32 多线程程序设计>书中p294中说:
1:
MFC有一个重大限制,会影响你所做的几乎每一件事情,MFC各对象和Win32 handles之间的映射关系记录在线程局部存贮中(TLS),因此你没有办法把一个MFC对象从某个线程手上交到另一个线程手上,你也不能在线程之间传递MFC对象指针.这里的指针包含(担不限于)CWnd,CDC,CPen,CBrush,CFont,Cbitmap,CPalette........
2:这个限制的意思是说,你不能够放一个指针(指向一个CWnd)到一个结构中,而该结构被一个Worker线程使用,你也不能够把一个指向CDialog或CView的指针交给另一个线程.............在线程之间共享对象,这里倒有一个不大方便的替代方案,不要放置MFC对象,改放对象的handle,你可以利用GetSateHwnd()获得派生自CWnd对象的handle,CDialog.

上各程序中
void CTestDlg::OnBtStart()
{
       
        Info.p_Progress=&m_progress; //传递进度条的地址,不对吧?? 进度条派生自CWnd
        Info.p_Dlg=this;             //传递对话框的指针,不对吧?? 进度条派生自CWnd
             Info.second=1;
        AfxBeginThread(ThreadProc,&Info);
}




threadInfo Info;
UINT ThreadProc(LPVOID pParam)
{
        threadInfo * p=(threadInfo *)pParam;
        for(int i=0;i<=100;i++)
        {
                p->p_Progress->SetPos(i);//利用所传进度条的地址,调用进度条的成员函数,不对吧??.
                ::Sleep(p->second*10);


        }
        ::PostMessage(p->p_Dlg->GetSafeHwnd(),USER_PROC_FINISHED,0,0);
        //(p->p_Dlg->GetSafeHwnd() 利用所传对话框的this指针,调用成员函数GetSafeHwnd(),不对吧??.
        return 0;
}

回复

使用道具 举报

0

主题

15

帖子

13.00

积分

新手上路

Rank: 1

积分
13.00
发表于 2020-9-26 15:00:01 | 显示全部楼层
国内的书有很不认真的. 侯捷是对的, MSDN上也是这么说的.

通过传递HANDLE而不是对象的指针, 你可以在你的worker thread中用CWnd::FromHandle或CWnd::Attach来将HANDLE绑定到在你这个worker thread中的一个C++对象上.

还可以通过给主线程PostMessage来完成一定的工作
回复

使用道具 举报

1

主题

7

帖子

5.00

积分

新手上路

Rank: 1

积分
5.00
 楼主| 发表于 2020-9-26 22:45:01 | 显示全部楼层

void CTestDlg::OnBtStart()
{
        AfxBeginThread(ThreadProc,GetSafeHwnd());

}

UINT ThreadProc(LPVOID pParam)
{
        CTestDlg  testdlg;
        testdlg.Attach((HWND)pParam);
        for(int i=0;i<=100;i++)
        {
                    testdlg.m_progress.SetPos(i);//在辅助线程中操作进度条
                  Sleep(1*10);
        }
        ::PostMessage((HWND)pParam,USER_PROC_FINISHED,0,0);
        return 0;
}

兄弟 我把相应的代码改为上面,怎么有出现运行断言.点击IDC_BT_START按钮出现断言.
回复

使用道具 举报

0

主题

15

帖子

13.00

积分

新手上路

Rank: 1

积分
13.00
发表于 2020-9-26 23:15:01 | 显示全部楼层
你确定m_progress确实拥有一个有效的m_hWnd吗?

或者是不是没有testdlg.Detach()的原因?
回复

使用道具 举报

1

主题

7

帖子

5.00

积分

新手上路

Rank: 1

积分
5.00
 楼主| 发表于 2020-9-26 23:30:01 | 显示全部楼层
UINT ThreadProc(LPVOID pParam)
{
         CTestDlg  testdlg;
        testdlg.Attach((HWND)pParam);
        for(int i=0;i<=100;i++)
        {
                  testdlg.m_progress.SetPos(i);
                Sleep(1*10);
        }       
       
        testdlg.Detach();
        ::PostMessage((HWND)pParam,USER_PROC_FINISHED,0,0);

        return 0;
}

改为这样还是不行呀??
回复

使用道具 举报

1

主题

7

帖子

5.00

积分

新手上路

Rank: 1

积分
5.00
 楼主| 发表于 2020-9-26 23:45:01 | 显示全部楼层
你确定m_progress确实拥有一个有效的m_hWnd吗?

对兄弟 你说对我.我试了,的确是您说的.

void CTestDlg::OnBtStart()
{
        AfxBeginThread(ThreadProc,this>m_progress.GetSafeHwnd());
}

UINT ThreadProc(LPVOID pParam)
{
        CProgressCtrl progress1;
        progress1.Attach((HWND)pParam);
        for(int i=0;i<=100;i++)
        {
       
                  progress1.SetPos(i);
                Sleep(1*10);
        }       
        ::PostMessage(progress1.GetParent()->GetSafeHwnd(),USER_PROC_FINISHED,0,0);
         progress1.Detach();
                return 0;
}

改为如下 可以了.
       
回复

使用道具 举报

1

主题

7

帖子

5.00

积分

新手上路

Rank: 1

积分
5.00
 楼主| 发表于 2020-9-27 01:30:02 | 显示全部楼层
UINT ThreadProc(LPVOID pParam)
{       
         CProgressCtrl*  p_progress=(CProgressCtrl*)(CProgressCtrl::FromHandle((HWND)pParam));
        for(int i=0;i<=100;i++)
        {
       
                  p_progress->SetPos(i);
                Sleep(1*10);
        }       
       
        ::PostMessage(p_progress->GetParent()->GetSafeHwnd(),USER_PROC_FINISHED,0,0);
        return 0;

}
如上亦可行
回复

使用道具 举报

0

主题

10

帖子

9.00

积分

新手上路

Rank: 1

积分
9.00
发表于 2020-9-27 06:30:01 | 显示全部楼层

testdlg.Attach((HWND)pParam);
一个窗口应该只能被一个对象Attach吧.我跟踪了下,点击"开始"按钮之后的断言,就出现在这里.

这样做,可以使testdlg.Attach((HWND)pParam);运行成功:

AfxBeginThread(ThreadProc,this->Detach());
在现成结束之后,再 this->Attach...

但是同样没有任何意义,因为在testdlg Attach成功之后,监视可以发现,testdlg的成员变量m_progress1为NULL,这样运行到
testdlg.m_progress.SetPos(i);这句话时,是肯定会出现内存错误的.

其实说的是往线程传参数是,最好是handle,但是前面传指针,还不是用的好好的?干麻非要传handle呢?handle作为参数传进去的时候,好象做PostMessage(..)的第一个参数时,是没有什么问题的,至于为什么不能用他来操作窗口,那就要等牛人出现给个解释了!
回复

使用道具 举报

0

主题

10

帖子

9.00

积分

新手上路

Rank: 1

积分
9.00
发表于 2020-9-27 07:00:01 | 显示全部楼层
啊,这个帖子打开的时间太长了...
下去把程序调了下,再上来回复,没有刷新,没想到楼主自己都已经把问题解决了啊!

虚心学习ing...
回复

使用道具 举报

1

主题

7

帖子

5.00

积分

新手上路

Rank: 1

积分
5.00
 楼主| 发表于 2020-9-27 09:45:01 | 显示全部楼层
兄弟 我再进一步 在对话框新增加一个成员函数
class CTestDlg : public CDialog
{
public:
        void call();
     .....
}

void CTestDlg::call()
{
        for(int i=0;i<=100;i++)
        {
                m_progress.SetPos(i);
                Sleep(10);
        }
   
}

开始按钮的单击函数
void CTestDlg::OnBtStart()
{

        AfxBeginThread(ThreadProc,GetSafeHwnd());//传入对话框的句柄
}
在ThreadProc的线程函数中
UINT ThreadProc(LPVOID pParam)
{

        CTestDlg *p=(CTestDlg*)(CDialog::FromHandle((HWND)pParam));
        p->call();
        return 0;
}

这有出现问题了.就是p->call();利用转换后的指针调用它的成员函数,这怎么又不行了??
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|CopyRight © 2008-2023|verysource.com ( 京ICP备17048824号-1 )

快速回复 返回顶部 返回列表