如何在另一个线程中执行方法?

时间:2010-11-24 16:31:38

标签: c++ c multithreading design-patterns class-design

我正在寻找C或C ++中这个问题的解决方案 修改:澄清一下。这是在Linux系统上。特定于Linux的解决方案绝对没问题。跨平台不是一个问题。

我有一个在自己的线程中运行的服务。这个服务是一个有多种方法的类,其中一些方法需要在自己服务的线程中而不是在调用者的线程中运行。

目前我正在使用包装器方法创建具有输入和输出参数的结构,将结构插入队列并返回(如果“命令”是异步的)或等待其执行(如果“命令”是同步)。

在线程方面,服务唤醒,从队列中弹出一个结构,找出要执行的内容并调用适当的方法。

此实现有效,但添加新方法非常麻烦:定义包装器,带参数的结构和处理程序。我想知道是否有一种更直接的方法来编写这种模型:一种类方法,它在类自己的线程上执行,而不是在调用者的线程中执行。

编辑 - 有点结论:
似乎没有事实上的方式来实现我所要求的不需要额外编码工作的方法。
我会坚持我的想法,它确保类型安全,最小化锁定,允许同步和异步调用以及相当适度的开销。
另一方面,它需要一些额外的编码,并且随着方法数量的增加,调度机制可能变得臃肿。在构造上注册调度方法,或者让包装器执行该工作似乎可以解决问题,消除一些开销并删除一些代码。

5 个答案:

答案 0 :(得分:2)

此问题的标准参考是here

  

使用条件变量实现线程安全队列

正如@John所说,这使用了Boost.Thread。

我要小心你在这里描述的同步案例。如果生产者(发送线程)等待来自消费者(服务线程)的结果,则很容易得到性能问题。如果您获得1000个异步调用,使用积压填充队列,然后是来自每个生产者线程的同步调用,会发生什么?在队列积压清除之前,您的系统将“死机”,从而释放这些同步呼叫者。如果可以的话,尝试仅使用异步来解耦它们。

答案 1 :(得分:1)

根据您要接受的复杂程度,有多种方法可以实现此目的。代码的复杂性与所需的灵活性成正比。这是一个简单的(并且使用得很好):

定义与服务器公开的每个功能相对应的类。 这些类中的每一个都实现一个名为execute的函数,并采用一个名为input args和output args的基本结构。

在服务寄存器中,这些方法类在初始化时注册。 一旦请求进入该线程,它将只有两个args,Input和Ouput,它们是更专业的参数的基类,是不同方法类所必需的。

然后你将服务类写成仅仅是委托,它接收传入的请求并根据ID或方法的名称(在初始注册期间使用)传递给相应的方法类。

我希望它有意义,这个方法的一个很好的例子是在XmlRpc ++(XmlRpc的c ++实现,你可以从sourceforge获取源代码)。

回顾一下:

struct Input {
  virtual ~Input () = 0;
};

struct Ouput {
  virtual ~Output () = 0;
};

struct MethodInterface {
   virtual int32_t execute (Input* __input, Output* __output)  = 0;
};

// Write specialized method classes and taking specialized input, output classes
class MyService {


  void registerMethod (std::string  __method_name, MethodInterface* __method);
  //external i/f
  int32_t execute (std::string __method, Input* __input, Output* __output);
};

您仍将使用队列机制,但不需要任何包装器。

答案 2 :(得分:1)

恕我直言,如果你想要解耦方法执行和线程上下文,你应该使用活动对象模式(AOP)

但是,您需要使用ACE Framework,它支持许多操作系统,例如Windows,Linux,VxWorks

您可以找到详细信息here

此外,AOP是命令,代理和观察者模式的组合,如果你知道它们的细节,你可以实现自己的AOP。希望它有所帮助

答案 3 :(得分:0)

除了使用Boost.Thread之外,我还会看一下boost :: function和boost :: bind。也就是说,将无类型(void)参数传递给目标方法似乎是公平的,并且让这些方法转换为正确的类型(C#等语言的典型习惯用法)。

答案 4 :(得分:0)

嘿,现在拉吉夫吉,我想你是颠倒了。代码的复杂性反向与灵活性成比例。您的数据结构和算法越复杂,您对可接受的输入和行为的限制就越多。

OP:你的描述似乎是完全一般的唯一解决方案,尽管它有不同的编码。最简单的可能是从:

派生一个类
struct Xqt { virtual void xqt(){} virtual ~Xqt(){} };

然后有一个指向Xqt的线程安全指针队列。然后服务线程将队列弹出到px并调用px-> xqt(),然后删除px。最重要的派生类是这一个:

  struct Dxqt : Xqt { 
    xqt *delegate; 
    Dxqt(xqt *d) : delegate(d) {}
    void xqt() { delegate->xqt(); }
  };

因为“计算机科学中的所有问题都可以通过一个更多级别的间接解决”,特别是这个类不会删除委托。这比使用标志要好得多,例如,确定服务器线程是否应该删除闭包对象。