如果以下代码解锁后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 :(得分:6)
不,这不是原子的。
事实上,在您解锁互斥锁之后,上下文切换可能特别可能,因为操作系统知道该互斥锁是否阻止了另一个线程。 (另一方面,操作系统甚至不知道您是否正在执行内联函数。)
答案 1 :(得分:4)
内联函数不是自动原子的。 inline
关键字仅表示“在编译此代码时,尝试通过使用来自调用主体的汇编指令替换调用来优化调用。”就像在任何其他汇编指令中一样,您可以在任何这些汇编指令上进行上下文切换,因此您需要使用锁来保护代码。
答案 2 :(得分:4)
inline
是一个编译器提示,建议编译器将代码内联到调用者而不是使用函数调用语义。但是,这只是一个暗示,并不总是需要注意。
此外,即使注意,结果是您的代码被内联到调用函数中。它不会将您的代码转换为原子序列的指令。
答案 3 :(得分:1)
Inline使函数像宏一样工作。内联与任何方式都与原子无关。
AFAIK inline是一个提示,gcc可能会忽略它。内联时,你的内联函数的代码,称之为B,被复制到调用者函数A中。没有从A到B的调用。这可能会使你的exe更快,但代价是更大。大概?如果你的内联函数很小,exe可能会变小。如果内联使得优化func 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个操作码。我猜这不是原子的。 int和longs需要一个操作码来设置。可能是原子的。 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 :(得分:0)
大多数语句不是原子的。您的线程可能会在++i
操作过程中被中断。规则是任何东西都不是原子的,除非它明确定义为原子。