Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 同步运行的自旋锁_C_Multithreading_Locking - Fatal编程技术网

C 同步运行的自旋锁

C 同步运行的自旋锁,c,multithreading,locking,C,Multithreading,Locking,假设我们有这些全局变量: int x = 0; int y = 0; spinlock_t* lock; 假设这两个线程同时运行 void thread1(void) { y++; spin_lock(lock); x++; spin_unlock(lock): } void thread2(void) { spin_lock(lock); x++; spin_unlock(lock); y++; } 我得到的结果是y=1和x

假设我们有这些全局变量:

int x = 0;
int y = 0;
spinlock_t* lock;
假设这两个线程同时运行

void thread1(void)
{
    y++;
    spin_lock(lock);
    x++;
    spin_unlock(lock):
}

void thread2(void)
{
    spin_lock(lock);
    x++;
    spin_unlock(lock);
    y++;
}

我得到的结果是
y=1
x=2
,但我希望x和y都等于2。为什么会发生这种情况?

请考虑以下执行顺序:

  • 线程2锁定锁
  • 线程2从
    x
    读入寄存器A
  • 线程2递增寄存器A
  • 线程2将寄存器A写入
    x
    ,它现在包含
    1
  • 螺纹2解锁锁
  • 线程2从
    y
    读入寄存器A
  • 线程2递增寄存器A
  • 线程1从
    y
    读入寄存器B
  • 线程2将寄存器A写入
    y
    ,它现在包含
    1
  • 线程1递增寄存器B
  • 线程1将寄存器B写入
    y
    ,它现在包含
    1
  • 线程1锁定锁
  • 线程1从
    x
    读入寄存器A
  • 线程1递增寄存器A
  • 线程1将寄存器A写入
    x
    ,它现在包含
    2
  • 螺纹1解锁锁
  • 由于没有锁来保护对
    y
    的访问,步骤6到11中的混合操作序列导致
    y
    只增加
    1
    而不是
    2
    。这就是当多个线程修改非原子变量时需要锁定的原因


    在一个线程中的锁定区域之前和另一个线程中的锁定区域之后递增
    y
    不能确保互斥,因为您无法确定线程1将尝试在线程2之前运行。

    您没有指定正在使用的线程模型或标准。但典型的标准规定,如果您在一个线程中访问一个对象,而另一个线程正在或可能正在修改它,则这是未定义(或未指定)的行为

    显然,您可以使用
    y
    来实现这一点。所以你没有理由期待任何特别的结果——任何事情都有可能发生。对于某些线程标准,它可能崩溃或产生任何结果。对于其他代码,
    y
    的最终值是未指定的,但是该代码在其他情况下可以保证实现您期望的功能

    不要陷入关于特定硬件(如缓存和寄存器)可以做什么或可能做什么的推理中。这与这个问题的答案完全无关,因为它适用于任何您正在编写遵循规范或标准的代码的问题。你不能通过找不到任何可能在某些特定硬件上失败的方法来证明代码是安全的

    更重要的是,您不能通过找到一种在某些特定硬件或平台上失败的方法来证明代码是不安全的。(这就是为什么巴纳尔的答案是错误的。)我们总是可以想象代码的执行方式会导致失败——编译器的工作是不以这种方式编译代码,除非代码本身有问题

    例如,考虑这个代码:

    int i;
    
    void foo()
    {
        i++;
        bar();
        i++;
    }
    void bar()
    {
        i++;
    }
    
    认为
    foo
    可能只增加
    i
    两次是完全无效的,因为
    i
    的值可能在调用
    bar
    时保存在寄存器中,从而丢失
    bar
    i
    的更改。为什么?因为编译器只允许在寄存器中保存
    i
    ,前提是它可以证明这不会影响代码的输出,因为代码遵循所有标准规则

    只有在代码违反某些规则的情况下,才会对可能发生的情况进行推理,从而导致代码失败。否则,编译器的工作就是让代码不失败,不管发生什么

    Barmar的论点同样适用于上述代码,但很明显,代码不能失败。所以这个论点是无效的。代码要么遵循规则,要么不遵循规则。如果它不遵守规则,它就被破坏了,因为它不遵守规则。如果它确实遵循规则,那么编译器和平台的工作就是确保它不会以某种方式失败


    据推测,代码被破坏是因为它没有遵循线程平台的规则或它所编码的标准。但我们不能确定,因为您还没有告诉我们该平台或标准是什么。

    为什么不呢?请详细解释。在两个线程->未定义的行为中,对
    y
    的非同步非原子、非只读访问。我假设您不会一直得到相同的结果,但有时也会看到2,2?这是一个伪代码作业问题-OP实际上没有尝试过它…每20亿次,增加y将失败。”我得到的结果是y=1和x=2’,这是非常误导人的。你是说
    y
    <密码>x被锁保护着。啊,我的答案倒过来了!哦,没我想的那么糟,我把它们列在了步骤列表中。“这就是为什么当有多个线程修改一个非原子变量时需要锁定。”这没有意义。当且仅当OP使用的任何线程模型的标准表示需要锁定时,才需要锁定。如果标准说需要,这就是为什么需要它。OP没有提到任何特定的平台或标准,因此您声称需要锁是没有根据的。例如,他可以使用一个平台,其中所有变量都是可变的,所有多线程都是协作的。他的CPU可能没有寄存器,对内存执行所有操作。我在回答中反驳了你的回答。除非你根据特定的平台和线程模型定制代码,否则你必须考虑到线程可能以任何可能的顺序交错,只有互斥体才能控制线程之间的交互。所以我所要做的就是显示一个可能导致共享变量行为不正确的顺序。@Barmar当然,线程可以按任何可能的顺序交错,但这是错误的