使用 ::AfxBeginThread(ThreadFunc, &INDEX); 时如何传递参数

Posted

技术标签:

【中文标题】使用 ::AfxBeginThread(ThreadFunc, &INDEX); 时如何传递参数【英文标题】:How to pass an argument when using ::AfxBeginThread(ThreadFunc, &INDEX); 【发布时间】:2015-05-28 08:12:13 【问题描述】:

我在 MFC 中使用 ::AfxBeginThread 时遇到问题。

我尝试创建一个线程,这是我的代码。

void CMyoControllerView::OnCbnSelchangeComboFist()

    int nIndex = m_combo_Fist.GetCurSel();
    int INDEX;

    if(nIndex != CB_ERR) 
        CString str;  
        m_combo_Fist.GetLBText(nIndex, str); 

       if(str == "Left Click") 
           INDEX = 0;
        else if(str == "Double Click") 
           INDEX = 1;
        else if(str == "Right Click") 
           INDEX = 2;
        else if(str == "Wheel Click") 
           INDEX = 3;
        else 
           INDEX = 4;
       
       pThread_Fist = ::AfxBeginThread(ThreadFunc, &INDEX);
    

而 ThreadFunc 函数是...

UINT CMyoControllerView::ThreadFunc(LPVOID pParam)
   
   int index= (int)pParam;
   if(index == 0)  
   else if(index == 1)   
   ...


::AfxBeginThread(ThreadFunc, &INDEX)); 中有问题

我想将参数传递给“INDEX”(可能的值:0~4)。

但是,当我调试这段代码时,ThreadFunc 的“索引”变量中有垃圾值。 (值应该是0~4。)

我尝试使用 reinterpret_cast 、 reinterpret_cast 等。 但我无法获得正确的价值。

我应该改变什么才能在“index”变量中获得正确的值?

(ps。我不确定,但我认为这可能是 x64 或 x86 问题。所以我搜索了谷歌并没有得到任何结果。 我使用的是 Windows 8 x64。)

【问题讨论】:

【参考方案1】:

您传递参数INDEX 的方式很好。您遇到的问题是同步问题之一:INDEX 是一个自动变量,当它超出范围时,即在 CMyoControllerView::OnCbnSelchangeComboFist 返回之前变得无效。那时您的线程可能还没有读取变量,一旦读取,它就会看到垃圾。另外,您在尝试读取 int* 时将其重新解释为 int 值,因此您的 index 变量是 INDEX 变量地址的数值。

解决方案是实现同步。在请求创建一个新线程后,您必须等待它读取传递给它的数据。一种解决方案是使用事件。

创建全局event对象同步线程启动:

HANDLE g_hThreadRunning = ::CreateEvent( NULL, FALSE, FALSE, NULL );

更改您的线程创建代码以等待线程发出信号,表明它已完成读取数据 (WaitForSingleObject):

void CMyoControllerView::OnCbnSelchangeComboFist() 

    // ...

    pThread_Fist = ::AfxBeginThread( ThreadFunc, &INDEX );
    ::WaitForSingleObject( g_hThreadRunning, INFINITE );

在您的线程过程中,将同步对象设置为已发出信号,完成读取数据后 (SetEvent)。请注意,您必须以与传递参数时相同的方式解释pParam,即作为int*

UINT CMyoControllerView::ThreadFunc( LPVOID pParam )    
    int index = *static_cast<int*>( pParam );
    ::SetEvent( g_hThreadRunning );

    if(index == 0)  
    else if(index == 1)   
    // ...


另一种方法是让 pParam 指针参数携带 @​​987654338@ 代替:
pThread_Fist = ::AfxBeginThread( ThreadFunc, reinterpret_cast<void*>( INDEX ) );

ThreadFunc内部:

int index = reinterpret_cast<int>( pParam );

由于这是按值传递参数,因此您不需要任何同步。但是,这并不普遍适用。特别是,它可能会在某些平台上失败。不能保证它可以正常工作(尽管它适用于所有运行 Windows 今天的平台)。

【讨论】:

另一种常见的模式是分配一个对象来保存你想用new传递给线程的信息,把对象的地址传递给线程,然后让线程delete它当它完成时。 @David:虽然很常见,但传递所有权通常是个坏主意。目标线程可能使用与源线程不同的内存分配器,从而导致内存损坏等细微错误。 线程的全部意义,它们存在的原因,是它们共享所有内存。如果您的平台不允许您利用这一点,并且要求您保留具有特定线程的对象的所有权,那么您有点错过了重点。绝大多数使用线程的代码在线程之间传递所有权。例如,您甚至不能使用互斥锁来保护向量。 (如果向量重新分配会发生什么?!)我的意思是,你对线程所做的几乎所有事情都会被这条规则禁止!如果这不起作用,线程将无法使用,IMO。 @David:C++ 不知道线程(至少在 C++11 之前不知道,即便如此,支持也不完整)。通常,使用 C++ 分配器并传递 所有权 仍然是个坏主意。但是,为 access 传递一个指针是可以的。如果您需要传递所有权,请使用知道线程的显式内存分配器,例如IMalloc。互斥体的所有权是明确建立的(例如CreateMutex),根本不处理内存分配。 您基本上是在争辩说,绝大多数使用线程的代码都是错误的,包括(例如)所有允许多个线程在受保护的向量下修改向量的 C++11 之前的代码互斥体。如果这是你与世界对抗的一些宠物问题,那很好,但不要让你的宠物问题影响你给那些试图学习谁需要了解其他人如何做事的人的建议。【参考方案2】:

通过值转换将INDEX 传递给LPVOID

pThread_Fist = ::AfxBeginThread(ThreadFunc, (LPVOID)INDEX);

您现有的线程代码可以进行相反的转换:

UINT CMyoControllerView::ThreadFunc(LPVOID pParam)

     int index= (int)pParam;
     ...

LPVOID 的大小足以包含 x86 和 x64 平台的整数值。

【讨论】:

有趣的否定投票,没有任何 cmets 的简单而明显正确的答案。 C 风格强制转换为标记为 C++ 的问题。不普遍适用的解决方案。完全忽略指针和int 的潜在不同对齐规则。它是巧合,因此投反对票。 @IInspectable:这个解决方案是绝对正确的。我不想对你糟糕的同步解决方案投反对票,因为投票战是被禁止的。您稍后还复制了我对您问题的回答。请专注于提供好的解决方案,而不是与其他成员争吵。 为什么不在my answer上发表评论,指出同步为什么“可怕”,以及如何改进它。我修改了您的答案,并将其包含在我的答案中。指出,为什么不推荐。现在那是我不会称之为复制的东西。 @IInspectable:您的解决方案很糟糕,因为您建议使用同步将简单的整数值传递给线程。然后,您将我的答案复制到您自己的帖子中,并否决了我的答案。你认为这是相扑竞技场吗?

以上是关于使用 ::AfxBeginThread(ThreadFunc, &INDEX); 时如何传递参数的主要内容,如果未能解决你的问题,请参考以下文章

MFC 和 AfxBeginThread 的线程调度问题

MFC 多线程:AfxBeginThread 与 Boost.Thread?

如何将参数传递给afxbeginthread

没有重载函数“AfxBeginThread”的实例与参数列表匹配

CWinThread 由 AfxBeginThread 创建后谁拥有它?

使用MFC中的AfxBeginThread创建多线程