I'm looking for a solution for this problem in C or C++.
edit: To clarify. This is on a linux system. Linux-specific solutions are absolutely fine. Cross-plaform is not a concern.
我正在寻找C或C ++中这个问题的解决方案。编辑:澄清。这是在Linux系统上。特定于Linux的解决方案绝对没问题。跨平台不是一个问题。
I have a service that runs in its own thread. This service is a class with several methods, some of which need to run in the own service's thread rather than in the caller's thread.
我有一个在自己的线程中运行的服务。此服务是一个包含多个方法的类,其中一些方法需要在自己服务的线程中运行,而不是在调用方的线程中运行。
Currently I'm using wrapper methods that create a structure with input and output parameters, insert the structure on a queue and either return (if a "command" is asynchronous) or wait for its execution (if a "command" is synchronous).
目前我正在使用包装器方法创建具有输入和输出参数的结构,将结构插入队列并返回(如果“命令”是异步的)或等待其执行(如果“命令”是同步的)。
On the thread side, the service wakes, pops a structure from the queue, figures out what to execute and calls the appropriate method.
在线程方面,服务唤醒,从队列中弹出一个结构,找出要执行的内容并调用适当的方法。
This implementation works but adding new methods is quite cumbersome: define wrapper, structure with parameters, and handler. I was wondering if there is a more straightforward means of coding this kind of model: a class method that executes on the class's own thread, instead of in the caller's thread.
这种实现有效,但添加新方法非常麻烦:定义包装器,带参数的结构和处理程序。我想知道是否有一种更直接的方法来编写这种模型:一种类方法,它在类自己的线程上执行,而不是在调用者的线程中执行。
edit - kind of conclusion:
It seems that there's no de facto way to implement what I asked that doesn't involve extra coding effort.
I'll stick with what I came up with, it ensures type safeness, minimizes locking, allows sync and async calls and the overhead it fairly modest.
On the other hand it requires a bit of extra coding and the dispatch mechanism may become bloated as the number of methods increases. Registering the dispatch methods on construction, or having the wrappers do that work seem to solve the issue, remove a bit of overhead and also remove some code.
编辑 - 一种结论:似乎没有事实上的方法来实现我所要求的,不涉及额外的编码工作。我会坚持我提出的方法,它确保类型安全,最小化锁定,允许同步和异步调用以及相当适度的开销。另一方面,它需要一些额外的编码,并且随着方法数量的增加,调度机制可能变得臃肿。在构造上注册调度方法,或者让包装器执行该工作似乎可以解决问题,消除一些开销并删除一些代码。
5 个解决方案
#1
1
There are several ways to achieve this, depending upon the complexity you want to accept. Complexity of the code is directly proportional to the flexibility desired. Here's a simple one (and quite well used):
有几种方法可以实现这一点,具体取决于您要接受的复杂程度。代码的复杂性与所需的灵活性成正比。这是一个简单的(并且使用得很好):
Define a classes corresponding to each functionality your server exposes. Each of these classes implements a function called execute and take a basic structure called input args and output args.
定义与服务器公开的每个功能相对应的类。这些类中的每一个都实现一个名为execute的函数,并采用一个名为input args和output args的基本结构。
Inside the service register these methods classes at the time of initialization. Once a request comes to the thread, it will have only two args, Input and Ouput, Which are the base classes for more specialized arguments, required by different method classes.
在服务内部,在初始化时注册这些方法类。一旦请求进入线程,它将只有两个args,Input和Ouput,它们是不同方法类所需的更专业参数的基类。
Then you write you service class as mere delegation which takes the incoming request and passes on to the respective method class based on ID or the name of the method (used during initial registration).
然后,您将服务类编写为纯粹的委托,它接收传入请求并根据ID或方法名称(在初始注册期间使用)传递给相应的方法类。
I hope it make sense, a very good example of this approach is in the XmlRpc++ (a c++ implementation of XmlRpc, you can get the source code from sourceforge).
我希望它有意义,这个方法的一个很好的例子是在XmlRpc ++(XmlRpc的c ++实现,你可以从sourceforge获取源代码)。
To recap:
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);
};
You will still be using the queue mechanism, but you won't need any wrappers.
您仍将使用队列机制,但不需要任何包装器。
#2
2
My standard reference for this problem is here.
这个问题的标准参考是在这里。
Implementing a Thread-Safe Queue using Condition Variables
使用条件变量实现线程安全队列
As @John noted, this uses Boost.Thread.
正如@John所说,这使用了Boost.Thread。
I'd be careful about the synchronous case you described here. It's easy to get perf problems if the producer (the sending thread) waits for a result from the consumer (the service thread). What happens if you get 1000 async calls, filling up the queue with a backlog, followed by a sync call from each of your producer threads? Your system will 'play dead' until the queue backlog clears, freeing up those sync callers. Try to decouple them using async only, if you can.
我对你在这里描述的同步案例要小心。如果生产者(发送线程)等待来自消费者(服务线程)的结果,则很容易出现性能问题。如果您获得1000个异步调用,使用积压填充队列,然后是来自每个生产者线程的同步调用,会发生什么?在队列积压清除之前,您的系统将“玩死”,从而释放这些同步呼叫者。如果可以的话,尝试仅使用异步来解耦它们。
#3
1
IMHO, If you want to decouple method execution and thread context, you should use Active Object Pattern (AOP)
恕我直言,如果你想要解耦方法执行和线程上下文,你应该使用活动对象模式(AOP)
However, you need to use ACE Framework, which supports many OSes, e.g. Windows, Linux, VxWorks
但是,您需要使用ACE Framework,它支持许多操作系统,例如Windows,Linux,VxWorks
You can find detailed information here
您可以在此处找到详细信息
Also, AOP is a combination of Command, Proxy and Observer Patterns, if you know the details of them, you may implement your own AOP. Hope it helps
此外,AOP是命令,代理和观察者模式的组合,如果您知道它们的详细信息,您可以实现自己的AOP。希望能帮助到你
#4
0
In addition to using Boost.Thread, I would look at boost::function and boost::bind. That said, it seems fair to have untyped (void) arguments passed to the target methods, and let those methods cast to the correct type (a typical idiom for languages like C#).
除了使用Boost.Thread之外,我还会看一下boost :: function和boost :: bind。也就是说,将无类型(void)参数传递给目标方法似乎是公平的,并且让这些方法转换为正确的类型(C#等语言的典型习惯用法)。
#5
0
Hey now Rajivji, I think you have it upside-down. Complexity of code is inversely proportional to flexibility. The more complex your data structures and algorithms are, the more restrictions you are placing on acceptable inputs and behaviour.
嘿现在拉吉夫吉,我想你已经颠倒了。代码的复杂性与灵活性成反比。您的数据结构和算法越复杂,您对可接受的输入和行为的限制就越多。
To the OP: your description seems perfectly general and the only solution, although there are different encodings of it. The simplest may be to derive a class from:
OP:你的描述似乎是完全一般的和唯一的解决方案,尽管它有不同的编码。最简单的可能是从以下派生类:
struct Xqt { virtual void xqt(){} virtual ~Xqt(){} };
and then have a thread-safe queue of pointers to Xqt. The service thread then just pops the queue to px and calls px->xqt(), and then delete px. The most important derived class is this one:
然后有一个指向Xqt的线程安全指针队列。然后,服务线程将队列弹出到px并调用px-> xqt(),然后删除px。最重要的派生类是这一个:
struct Dxqt : Xqt { xqt *delegate; Dxqt(xqt *d) : delegate(d) {} void xqt() { delegate->xqt(); } };
because "all problems in Computer Science can be solved by one more level of indirection" and in particular this class doesn't delete the delegate. This is much better than using a flag, for example, to determine if the closure object should be deleted by the server thread.
因为“计算机科学中的所有问题都可以通过一个更多层次的间接解决”,特别是这个类不会删除委托。这比使用标志要好得多,例如,确定是否应该由服务器线程删除闭包对象。
#1
1
There are several ways to achieve this, depending upon the complexity you want to accept. Complexity of the code is directly proportional to the flexibility desired. Here's a simple one (and quite well used):
有几种方法可以实现这一点,具体取决于您要接受的复杂程度。代码的复杂性与所需的灵活性成正比。这是一个简单的(并且使用得很好):
Define a classes corresponding to each functionality your server exposes. Each of these classes implements a function called execute and take a basic structure called input args and output args.
定义与服务器公开的每个功能相对应的类。这些类中的每一个都实现一个名为execute的函数,并采用一个名为input args和output args的基本结构。
Inside the service register these methods classes at the time of initialization. Once a request comes to the thread, it will have only two args, Input and Ouput, Which are the base classes for more specialized arguments, required by different method classes.
在服务内部,在初始化时注册这些方法类。一旦请求进入线程,它将只有两个args,Input和Ouput,它们是不同方法类所需的更专业参数的基类。
Then you write you service class as mere delegation which takes the incoming request and passes on to the respective method class based on ID or the name of the method (used during initial registration).
然后,您将服务类编写为纯粹的委托,它接收传入请求并根据ID或方法名称(在初始注册期间使用)传递给相应的方法类。
I hope it make sense, a very good example of this approach is in the XmlRpc++ (a c++ implementation of XmlRpc, you can get the source code from sourceforge).
我希望它有意义,这个方法的一个很好的例子是在XmlRpc ++(XmlRpc的c ++实现,你可以从sourceforge获取源代码)。
To recap:
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);
};
You will still be using the queue mechanism, but you won't need any wrappers.
您仍将使用队列机制,但不需要任何包装器。
#2
2
My standard reference for this problem is here.
这个问题的标准参考是在这里。
Implementing a Thread-Safe Queue using Condition Variables
使用条件变量实现线程安全队列
As @John noted, this uses Boost.Thread.
正如@John所说,这使用了Boost.Thread。
I'd be careful about the synchronous case you described here. It's easy to get perf problems if the producer (the sending thread) waits for a result from the consumer (the service thread). What happens if you get 1000 async calls, filling up the queue with a backlog, followed by a sync call from each of your producer threads? Your system will 'play dead' until the queue backlog clears, freeing up those sync callers. Try to decouple them using async only, if you can.
我对你在这里描述的同步案例要小心。如果生产者(发送线程)等待来自消费者(服务线程)的结果,则很容易出现性能问题。如果您获得1000个异步调用,使用积压填充队列,然后是来自每个生产者线程的同步调用,会发生什么?在队列积压清除之前,您的系统将“玩死”,从而释放这些同步呼叫者。如果可以的话,尝试仅使用异步来解耦它们。
#3
1
IMHO, If you want to decouple method execution and thread context, you should use Active Object Pattern (AOP)
恕我直言,如果你想要解耦方法执行和线程上下文,你应该使用活动对象模式(AOP)
However, you need to use ACE Framework, which supports many OSes, e.g. Windows, Linux, VxWorks
但是,您需要使用ACE Framework,它支持许多操作系统,例如Windows,Linux,VxWorks
You can find detailed information here
您可以在此处找到详细信息
Also, AOP is a combination of Command, Proxy and Observer Patterns, if you know the details of them, you may implement your own AOP. Hope it helps
此外,AOP是命令,代理和观察者模式的组合,如果您知道它们的详细信息,您可以实现自己的AOP。希望能帮助到你
#4
0
In addition to using Boost.Thread, I would look at boost::function and boost::bind. That said, it seems fair to have untyped (void) arguments passed to the target methods, and let those methods cast to the correct type (a typical idiom for languages like C#).
除了使用Boost.Thread之外,我还会看一下boost :: function和boost :: bind。也就是说,将无类型(void)参数传递给目标方法似乎是公平的,并且让这些方法转换为正确的类型(C#等语言的典型习惯用法)。
#5
0
Hey now Rajivji, I think you have it upside-down. Complexity of code is inversely proportional to flexibility. The more complex your data structures and algorithms are, the more restrictions you are placing on acceptable inputs and behaviour.
嘿现在拉吉夫吉,我想你已经颠倒了。代码的复杂性与灵活性成反比。您的数据结构和算法越复杂,您对可接受的输入和行为的限制就越多。
To the OP: your description seems perfectly general and the only solution, although there are different encodings of it. The simplest may be to derive a class from:
OP:你的描述似乎是完全一般的和唯一的解决方案,尽管它有不同的编码。最简单的可能是从以下派生类:
struct Xqt { virtual void xqt(){} virtual ~Xqt(){} };
and then have a thread-safe queue of pointers to Xqt. The service thread then just pops the queue to px and calls px->xqt(), and then delete px. The most important derived class is this one:
然后有一个指向Xqt的线程安全指针队列。然后,服务线程将队列弹出到px并调用px-> xqt(),然后删除px。最重要的派生类是这一个:
struct Dxqt : Xqt { xqt *delegate; Dxqt(xqt *d) : delegate(d) {} void xqt() { delegate->xqt(); } };
because "all problems in Computer Science can be solved by one more level of indirection" and in particular this class doesn't delete the delegate. This is much better than using a flag, for example, to determine if the closure object should be deleted by the server thread.
因为“计算机科学中的所有问题都可以通过一个更多层次的间接解决”,特别是这个类不会删除委托。这比使用标志要好得多,例如,确定是否应该由服务器线程删除闭包对象。