2011-01-27 73 views
2

在下面的代码解锁后可以在Linux上下文切换,如果是这样,我们有一个问题,如果两个线程调用此内联函数是否为原子?

inline bool CMyAutoLock::Lock(
    pthread_mutex_t *pLock, 
    bool bBlockOk 
) 
throw() 
{ 
    Unlock(); 
    if (pLock == NULL) 
     return (false); 
// **** can context switch happen here ? ****/// 
    return ((((bBlockOk)? pthread_mutex_lock(pLock) : 
     pthread_mutex_trylock(pLock)) == 0)? (m_pLock = pLock, true) : false); 
} 
+0

请问您是否可以格式化您的代码 – Elalfer 2011-01-27 04:27:23

+0

修复了代码格式和标题。 – 2011-01-27 04:28:12

回答

1

Inline使功能像宏一样工作。内联与原子无关。

AFAIK inline是一个提示,gcc可能会忽略它。在内联时,内联func中的代码称为B,它被复制到调用者函数A中。A和B之间不会有任何调用。这可能会让exe更快,但代价更大。大概?如果你的内联函数很小,exe可能会变小。如果内联使优化函数A变得更加困难,则exe可能会变得更慢。如果不指定内联,gcc将在很多情况下为您进行内联决策。类的成员函数是默认的内联。您需要明确告诉gcc不要执行自动内联。另外,当优化关闭时,gcc不会内联。

链接器不会内联。因此,如果模块A extern引用了标记为内联的函数,但代码位于模块B中,则模块A将调用该函数而不是内联它。您必须在头文件中定义函数,并且必须将其声明为extern inline func foo(a,b,c)。 Its actually a lot more complicated.

inline void test(void); // __attribute__((always_inline)); 

inline void test(void) 
{ 
    int x = 10; 
    long y = 10; 
    long long z = 10; 
    y++; 
    z = z + 10; 
} 
int main(int argc, char** argv) 
{ 
    test(); 
    return (0); 
} 

不是内联:

!{ 
main+0: lea 0x4(%esp),%ecx 
main+4: and $0xfffffff0,%esp 
main+7: pushl -0x4(%ecx) 
main+10: push %ebp 
main+11: mov %esp,%ebp 
main+13: push %ecx 
main+14: sub $0x4,%esp 
! test(); 
main+17: call 0x8048354 <test> <--- making a call to test. 
! return (0); 
main() 
main+22: mov $0x0,%eax 
!} 
main+27: add $0x4,%esp 
main+30: pop %ecx 
main+31: pop %ebp 
main+32: lea -0x4(%ecx),%esp 
main+35: ret  

内联:

inline void test(void)__attribute__((always_inline)); 


! int x = 10; 
main+17: movl $0xa,-0x18(%ebp)     <-- hey this is test code....in main()! 
! long y = 10; 
main+24: movl $0xa,-0x14(%ebp) 
! long long z = 10; 
main+31: movl $0xa,-0x10(%ebp) 
main+38: movl $0x0,-0xc(%ebp) 
! y++; 
main+45: addl $0x1,-0x14(%ebp) 
! z = z + 10; 
main+49: addl $0xa,-0x10(%ebp) 
main+53: adcl $0x0,-0xc(%ebp) 
!} 
!int main(int argc, char** argv) 
!{ 
main+0: lea 0x4(%esp),%ecx 
main+4: and $0xfffffff0,%esp 
main+7: pushl -0x4(%ecx) 
main+10: push %ebp 
main+11: mov %esp,%ebp 
main+13: push %ecx 
main+14: sub $0x14,%esp 
! test();       <-- no jump here 
! return (0); 
main() 
main+57: mov $0x0,%eax 
!} 
main+62: add $0x14,%esp 
main+65: pop %ecx 
main+66: pop %ebp 
main+67: lea -0x4(%ecx),%esp 
main+70: ret 

可以肯定的唯一功能是原子是gcc atomic builtins。可能很简单的一个操作码汇编指令也是原子的,但它们可能不是。根据我的经验,至今在6x86设置或读取32位整数是原子。您可以通过查看生成的汇编代码来猜测一行c代码是否可以是原子的。

上述代码是在32位模式下编译的。你可以看到,long long需要2个操作码加载。我猜这不是原子的。整数和长整数需要一个操作码设置。可能是原子的。 y ++是用addl实现的,这可能是原子的。我一直在说,可能是因为CPU上的微代码可能使用多个指令来实现一个操作,并且这个知识高于我的薪酬等级。我假设所有的32位写入和读取都是原子的。我认为增量不是,因为它们通常是通过读取和写入来执行的。

但检查了这一点,在64位

! int x = 10; 
main+11: movl $0xa,-0x14(%rbp) 
! long y = 10; 
main+18: movq $0xa,-0x10(%rbp) 
! long long z = 10; 
main+26: movq $0xa,-0x8(%rbp) 
! y++; 
main+34: addq $0x1,-0x10(%rbp) 
! z = z + 10; 
main+39: addq $0xa,-0x8(%rbp) 
!} 
!int main(int argc, char** argv) 
!{ 
main+0: push %rbp 
main+1: mov %rsp,%rbp 
main+4: mov %edi,-0x24(%rbp) 
main+7: mov %rsi,-0x30(%rbp) 
! test(); 
! return (0); 
main() 
main+44: mov $0x0,%eax 
!} 
main+49: leaveq 
main+50: retq 

我猜addq可能是原子在编译时。

4

内联函数不会自动原子。关键字inline只是意味着“在编译此代码时,尝试通过用来自呼叫主体的汇编指令替换呼叫来优化掉呼叫。”您可以像任何其他汇编指令一样,在任何汇编指令中获得上下文切换输出,因此您需要用锁来防范代码。

+3

它甚至不再意味着它,它只是意味着“忽略此功能的ODR”。 – GManNickG 2011-01-27 04:32:09

6

不,它不是原子的。

事实上,它可能是特别可能是上下文切换发生在解锁互斥锁后发生,因为操作系统知道另一个线程是否在该互斥锁上被阻塞。 (在另一方面,操作系统甚至不知道是否你执行一个内联函数)。

4

inline是一个编译暗示,该表明编译器内联代码到调用,而不是使用功能调用语义。然而,这只是一个暗示,并不总是值得注意。

此外,即使注意,结果是您的代码被内联到调用函数中。它不会将您的代码转换为指令的原子序列。

0

大多数陈述不是原子的。您的线程可能会在++i操作中被中断。规则是任何东西都不是原子的,除非它是明确定义为原子的。