如何通过PostThreadMessage使用shared_ptr?

问题描述:

我想升级我的MFC生产代码,以在调用其他窗口或线程时使用std::shared_ptr智能指针.这样的调用是SendMessagePostMessagePostThreadMessage,它们通过wparamlparam,分别是unsigned intlong.当前,我创建一个类对象,新建一个对象,进行调用,将指针传递给该对象,在接收端使用该对象,然后将其删除.

I would like to upgrade my MFC production code to use the std::shared_ptr smart pointer when calling other windows or threads. Such calls are SendMessage, PostMessage and PostThreadMessage which pass wparam and lparam and which respectively are an unsigned int and long. Currently, I create a class object, new an object, make the call passing a pointer to the object, use the object on the receiving end and then delete it.

由于shared_ptr在我的其余代码中运行良好,因此我至少希望探讨为什么无法对Windows调用执行相同操作的原因.

Since shared_ptr works so well in the rest of my code I wanted to at least explore the reasons why I can't do the same for windows calls.

当前通话:

auto myParams = new MyParams(value1, value2, value3);
PostThreadMessage(MSG_ID, 0, reinterpret_cast< LPARAM >( myParams );

ReceivingMethod::OnMsgId( WPARAM wParam, LPARAM lParam)
{
  auto myParams = reinterpret_cast< MyParams * >( lParam );
  ... // use object
  delete myParams;
}

类似于C ++ 11的智能指针调用:

to a C++11-like smart pointer call:

std::shared_ptr< MyParams > myParams( new MyParams( value1, value2, value3 ) );
PostThreadMessage( MSG_ID, 0, ???myParams??? );

ReceivingMethod::OnMsgId( WPARAM wParam, LPARAM lParam )
{
  auto myParams = ???lParam???;
  ... // use object
}

@Remy Lebeau:这是经过修改的示例代码,以使用unique_ptr传递方法,但是,传递对象时我的代码存在泄漏:

@Remy Lebeau: Here is the sample code revised to use the unique_ptr passing approach, however, there are leaks in my code when passing the object:

struct Logger
{
  Logger()
  {
    errorLogger = ( ErrorLogger * )AfxBeginThread( RUNTIME_CLASS( ErrorLogger ), THREAD_PRIORITY_BELOW_NORMAL );
  }

  ~Logger()
  {
    // gets properly dtor'ed upon app exit
  }

  void MakeLogMsg( ... );

  ErrorLogger * errorLogger;
  std::unique_ptr< LogParams > logParams;
};

Logger logger;
std::recursive_mutex logParamsRecursiveMu; // because of multiple requests to lock from same thread

struct ErrorLogger : public CWinThread
{
  ErrorLogger()
  {
  }

  ~ErrorLogger()
  {
    // gets properly dtor'ed before logger upon app exit
  }

  afx_msg void OnLog( WPARAM wParam, LPARAM lParam );
};

void Logger::MakeLogMsg( ... )
{
  // construct msg from logparams 

  // make msg smart object using unique ptr and send to errorlogger thread queue
  logParams = std::make_unique< LogParams >();

  // set logparams

  // with the addition of the mutex guard, the leaks are gone
  logParamsRecursiveMu.lock();
  logger.errorLogger->PostThreadMessage( ONLOG_MSG, 0, reinterpret_cast< LPARAM >( logParams.get() ) );
  logParams.release(); // no longer owns object
  logParamsRecursiveMu.unlock();
}

void ErrorLogger::OnLog( WPARAM wParam, LPARAM lParam )
{
  std::unique_ptr< LogParams > logParams( reinterpret_cast< LogParams * >( lParam ) );
}

请注意,当我注释掉unique_ptr的传递时,泄漏消失了.我的代码与您使用这种方法的代码有什么不同?

Notice that when I comment out the passing of the unique_ptr, the leaks disappear. How does my code differ from your code that uses this approach and works?

Edit2:

关于@Remy Lebeau的答案,该答案显示了如何使用std::unique_ptr代替std::shared_ptr,我在下面的评论中指出没有……要实现的额外对象.没有明显的缺点.".好吧,那不是真的.必须为每种不同类型的消息创建MyParams对象.有些应用可能只有几种类型,但有些可能有100种或更多类型.每次我想在另一侧执行一个函数时,都必须制作一个新的结构,该结构具有一个接受目标调用的所有参数的构造函数.实施起来非常繁琐,而且如果维护起来非常繁琐.

With regard to @Remy Lebeau‘s answer showing how std::unique_ptr could be used instead of std::shared_ptr, I stated in a comment below that there were "…no extra objects to implement. No obvious cons.". Well, that is not quite true. The MyParams object has to be created for each different type of message. Some apps might only have a few types, but some might have 100s or more. Each time I want to execute a function on the other side I have to craft a new struct which has a constructor that takes all the arguments of the destination call. Very tedious to implement and hard to maintain if there are many.

我认为,仅通过传递参数就可以消除该结构构建阶段.

I think that it would be possible to eliminate that struct-building phase by merely passing the arguments.

显然,有新的C ++ 1x构造可以帮助完成此任务.也许是std::forward_as_tuple,它在args中构造对参数的引用的元组,适合作为参数传递给函数."

Apparently there are new C++1x constructs that can help accomplish this. One is perhaps the std::forward_as_tuple which "Constructs a tuple of references to the arguments in args suitable for forwarding as an argument to a function."

对于我的应用程序,我通过对MyParams进行模板化解决了该问题,但是对于想要避免添加大量结构的任何人,他可能都希望使用元组等.

For my app I solved the problem by templatizing MyParams, but for anyone whose wants to avoid adding a lot of structs, he may want to look at using tuples and the like.

@RemyLebeau的答案编号1和@ rtischer8277的答案编号5都是正确的.不幸的是,*无法识别多个正确答案.这种*限制反映了一个错误的心理语言假设,即语言上下文对于同一语言组是通用的,但事实并非如此. (请参见Roger Harris关于固定代码语言神话的集成语言学简介",第34页).为了回应我的原始帖子,@ RemyLebeau根据发布的代码所描述的上下文回答了该问题,该上下文显示了new ed MyParams(有关更多说明,请参见编辑2 :).稍后在答案5(rtischer8277)中,我根据问题的原始措词亲自回答了该问题,该问题询问是否可以在使用PostThreadMessage的线程中使用std::shared_ptr.合理的结果是,我已将正确答案重新分配给@RemyLebeau,这是第一个正确答案.

Edit 3: Both @RemyLebeau's answer number 1 and @rtischer8277’s answer number 5 are correct. Unfortunately, * doesn’t recognize multiple correct answers. This * limitation reflects a flawed PsychoLinguistic assumption that linguistic context is universal for the same language group, which it is not. (see "Introduction to Integrational Linguistics" by Roger Harris on the fixed-code language myth, p.34). In response to my original posting, @RemyLebeau answered the question based on the context described by the posted code that shows a newed MyParams (see Edit 2: for more explanation). Much later in Answer 5 (rtischer8277), I answered the question myself based on the original wording of the question which asked if std::shared_ptr could be used across threads using PostThreadMessage. As a reasonable consequence, I have re-assigned the correct answer back to @RemyLebeau it being the first correct answer.

我在此帖子中添加了第三个合法答案.参见以 @Remy Lebeau和@ rtischer8277开头的第六个答案,到目前为止,我已经对我的原始帖子提交了两个答案…….此解决方案的作用是将跨线程访问转换为概念上简单的RPC(远程过程调用).尽管此 Answer 显示了如何使用future来控制所有权和同步,但并未显示如何创建具有任意数量参数的消息对象,该对象可以安全地在参数的任一侧使用. PostThreadMessage调用.该功能已在*的 Answer 中得到解决,以发出

I added a third legitimate answer to this posting. See the 6th Answer beginning with @Remy Lebeau and @rtischer8277 have so far submitted two answers to my original posting…. The effect of this solution is to turn cross-thread access into the conceptually simple RPC (remote procedure call). Although this Answer shows how to use a future to control ownership and synchronization, it does not show how to create a message object with an arbitrary number of parameters that can safely be used on either side of the PostThreadMessage call. That functionality is addressed in *’s Answer to issue passing a parameter pack over a legacy function signature using forward_as_tuple.

由于消息参数必须超出调用范围,因此在本示例中使用shared_ptr没有多大意义,因为源shared_ptr是在处理邮件之前最有可能超出范围.我建议改用unique_ptr,这样您就可以在飞行过程中release()指针,然后在处理完消息后获得它的新所有权:

Since the message parameters have to outlive the calling scope, it does not make much sense to use shared_ptr in this example, as the source shared_ptr is most likely to go out of scope before the message is processed. I would suggest using unique_ptr instead, so that you can release() the pointer while it is in flight, and then obtain new ownership of it once the message is processed:

SendingMethod::SendMsgId( ... )
{
    ...

    std::unique_ptr<MyParams> myParams( new MyParams(value1, value2, value3) );
    if (PostThreadMessage(MSG_ID, 0, reinterpret_cast<LPARAM>(myParams.get()))
        myParams.release();

    ...
}

ReceivingMethod::OnMsgId( WPARAM wParam, LPARAM lParam)
{
    std::unique_ptr<MyParams> myParams( reinterpret_cast<MyParams*>(lParam) );
    ... // use object
}