简单的多线程代码需要澄清

时间:2012-07-05 13:03:03

标签: java c multithreading

我在线程上遇到了这个小问题。

    int x = 0;
    add() {

      x=x+1;
    }

如果我们在多个线程中运行它,比如4个线程,则每次x = 4的最终值,或者它可以是1,2,3或4.

由于

PS 让我们说添加的原子操作是这样的,

LOAD A x
ADD A 1 
LOAD x A

然后最终结果将是4.我是对的还是我错了什么?

3 个答案:

答案 0 :(得分:3)

这是数据竞赛的典型例子。

现在,让我们仔细看看add()的作用:

add()
{
   x = x + 1;
}

这转换为:

  1. 向我提供X的最新值并将其存储在我的私人工作区中
  2. 将1添加到存储在我的私有工作区中的值
  3. 将我工作区中的内容复制到我复制的内存中(可全局访问)。
  4. 现在,在我们进一步解释之前,你有一个名为context switch的东西,这是你的操作系统在不同的线程和进程之间划分处理器时间的过程。这个过程通常会给你的线程一个有限的处理器时间(在windows上大约40毫秒),然后中断工作,复制处理器在其寄存器中的所有内容(从而保留它的状态)并切换到下一个任务。这称为Round-robin task scheduling

    您无法控制何时处理中断并转移到另一个线程。

    现在想象你有两个线程做同样的事情:

    1. Give me the most recent value of X and store it in my private workspace
    2. Add 1 to that value that is stored in my private workspace
    3. Copy what I have in my workspace to the memory that I copied from (that is globally accessible).
    
    在任何一个运行之前,

    和X等于1。

    第一个线程可能会执行第一条指令并在其私有工作空间中存储X值,该值在其处理时最近 - 1.然后发生上下文切换,操作系统中断您的线程并给出控制到队列中的下一个任务,恰好是第二个线程。第二个线程还读取X的值,该值等于1.

    第二个线程设法运行完成 - 它将“下载”和“上传”计算值的值加1。

    操作系统再次强制进行上下文切换。

    现在第一个线程在被中断的点继续执行。它仍然会认为最近的值是1,它会将该值递增1并将其计算结果保存到该内存区域。这就是数据竞争的发生方式。您希望最终结果为3,但它是2。

    有许多方法可以避免此问题,例如locks/mutexescompare and swapatomic operations

答案 1 :(得分:2)

您的代码分为两个级别:

  1. 线程操作之间没有happens-before关系;
  2. 未强制执行get-and-increment的原子性。
  3. 要解决1.您可以添加volatile修饰符。这仍将使操作非原子化。为确保原子性,您可以使用(最好)AtomicIntegersynchronized(涉及锁定,不是首选)。

    按照目前的情况,如果从没有参与递增的线程中读取,结果可以是 0到4之间的任何数字

答案 2 :(得分:2)

多线程应用程序是并发的(这是重点)。

t1: LOAD A1 x
t2: LOAD A2 x
t3: LOAD A3 x
t4: LOAD A4 x
t1: ADD A1 1 
t2: ADD A2 1 
t3: ADD A3 1 
t4: ADD A4 1 
t1: STORE x A1
t2: STORE x A2
t3: STORE x A3
t4: STORE x A4

A1,A2,A3,A4是本地寄存器。

结果为1,但可能是234。如果您有其他线程,则由于可见性问题可能会看到旧值,请参阅0