我在C和C ++中都有这样的情况,可以用像装饰器这样的Python来解决这个问题:我很少有一些函数,我想用其他东西包装,以便在函数进入之前语句是执行的,当它离开时,执行其他一些功能。
例如,我在库C文件中有一些函数,当调用它们时应锁定信号量,并在将控件返回给被调用者之前,应释放信号量。没有锁,他们有以下结构:
int f1(int)
{
...
...
}
int f2(char*)
{
....
}
int f3(blabla)
{
....
}
... fn(...)
我想定义一个全局信号量,在返回函数之前调用和释放每个函数之前应该锁定它。我想尽可能简单地做到这一点;接近这一点:
#lockprotected
int f1(int)
{
... /* nothing changed over here */
}
#endlockprotected
或类似
int f1(int)
{
... /* nothing changed over here */
}
#lockprotected f1
我不想要的是:
最优雅的方式是什么?
答案 0 :(得分:16)
使用RAII(资源获取是初始化)来定义互斥锁上的锁定。这将允许您忘记第2点,即,您不需要跟踪return语句以释放锁定。
class Lock {
public:
Lock () { // acquire the semaphore }
~Lock () { // release the semaphore }
}
接下来,在函数的开头创建此类的对象,即
int f1 (int) {
Lock l;
// forget about release of this lock
// as ~Lock() will take care of it
}
这方面的一个优点是,即使在f1()
抛出异常的情况下,您仍然不必担心释放锁。在退出函数之前,所有堆栈对象都将被销毁。
答案 1 :(得分:11)
如果你真的想要一个C解决方案,你可以使用像:
这样的宏#define LOCK lock( &yourglobalsemaphore )
#define UNLOCK unlock( &yourglobalsemaphore )
#define LOCKED_FUNCTION_ARG1(TRet, FuncName, TArg1, Arg1Name ) \
TRet FuncName( TArg1 Arg1Name ) { \
LOCK; \
TRet ret = FuncName##_Locked( Arg1Name ); \
UNLOCK; \
return ret \
} \
TRet FuncName##_Locked(TArg1 Arg1Name )
#define LOCKED_FUNCTION_ARG2(TRet FuncName, TArg1, Arg1Name, TArg2, Arg2Name) \
//...etc
但是每个参数计数都需要1个宏(函数应该有一个返回类型)。
示例用法:
LOCKED_FUNCTION_ARG1(int, f1, int, myintarg)
{
//unchanged code here
}
答案 2 :(得分:3)
像这样定义包装器:
class SemaphoreWrapper
{
private:
semaphore &sem;
public
SemaphoreWrapper(semaphore &s)
{
sem = s;
sem.lock();
}
~SemaphoreWrapper()
{
sem.unlock();
}
}
然后在每个函数中创建一个SemaphoreWrapper实例:
void func1()
{
SemaphoreWrapper(global_semaphore);
...
}
SemaphoreWrapper的构造函数和析构函数将负责锁定/解锁功能。
答案 3 :(得分:1)
您可以编写包装函数,例如:
int f1_locked(int x) {
lock(..);
int r=f1(x);
unlock(..);
return r;
}
使用预处理器,您可以节省一些工作。
修改强>
正如有人所说,最好将实现移动到库内部函数中,并将包装器呈现给库用户,例如:
// lib exports the wrapper:
int f1(int x) {
lock(..);
int r=f1_unlocked(x);
unlock(..);
return r;
}
// for library internal use only:
int f1_unlocked(int x) {
...
}
这还有一个额外的好处,即来自lib内部函数的调用不需要多余的锁定(这可能与否,这取决于......),例如:
void f2_unlocked() {
...
f1_unlocked();
...
}
void f2() {
lock();
f2_unlocked();
unlock();
}
答案 4 :(得分:1)
别。
你不能通过在这里和那里喷出一些锁来从单线程到多线程,并希望最好。
您确定没有两个共享全局变量的函数吗? C函数因使用静态分配的缓冲区而臭名昭着。
互斥锁通常不是可重入的。因此,如果您装饰f1
和f2
而另一个调用另一个,则会陷入僵局。当然,您可以使用更昂贵的可重入互斥锁。
在最好的时候,多线程很难。通常需要理解和调整执行流程。
我很难想象扔掉几把锁会有效。
这显然打消了这样一个事实,即如果函数不是用MT制作的,那么它们可能会更慢(使用所有那些互斥操作),因此你不会获得很多好处。
如果您确实需要信号量,请让客户端锁定它。他应该知道何时锁定,何时不知道。
答案 5 :(得分:0)
装饰器是一种语言特性,它为底层语义提供语法糖。您可以在C ++中获得的基础语义:只需适当地包装函数;你无法得到的语法糖。
更好的选择是创建一个支持您的用例的适当代码设计,例如:由ineritance和decorator pattern。这并不一定意味着类继承 - 模式也可以使用类模板来实现。
至于您的特定用例,已经发布了更好的替代方案。
答案 6 :(得分:0)
在我看来,你想要做的是Aspect Oriented Programming(AOP)。在C和C ++中几乎没有AOP框架,但从我几个月前看到的情况来看,我认为AspectC++项目提供了AOP概念的一个很好的实现。我没有在生产代码中测试它。