import java.util.concurrent.atomic.AtomicInteger import collection.parallel.CollectionConverters.* // This demo should help understanding the `add` method implementation in // src/main/scala/midterm22/Part8.scala. // TL;DR: it demonstrate that `+=` is not atomic. var id: Int = 0 val l = for _ <- (0 until 10).par yield id += 1 id l l.distinct.size // Try to execute the worksheet several times (by saving again and again). We // might get a number of distinct elements not equal to 10. Why? // // First, note that `id += 1` is equivalent to `val tmp = id; id = tmp + 1`. // I.e. the read and write are two separate operations; the `+=` operation is // not *atomic*. // // Then, consider the following execution: // - T1: reads 0 from id and store it in its local `tmp` var. // - T2: does the same: reads 0 from id and store it in its local `tmp` var. // - T1: computes `tmp + 1 == 1`, stores it in `id` and returns `1` // - T2: computes `tmp + 1 == 1`, stores it in `id` and returns `1` // // Now we have two times the ID 1! // The code we wrote before is equivalent to: var id2: Int = 0 val l2 = for _ <- (0 until 10).par yield val tmp = id2 id2 = tmp + 1 val tmp2 = id2 tmp2 l2 l2.distinct.size // * // How to make it correct? Use an atomic integer! val id3 = AtomicInteger() val l3 = for _ <- (0 until 10).par yield id3.incrementAndGet() l3 // Now we are sure that this will always be 10. l3.distinct.size // * assert(l3.distinct.size == 10) // Or, using only what has been seen before the midterm this year: another // solution is to use synchronize: var id4: Int = 0 val lock = Object() val l4 = for _ <- (0 until 10).par yield lock.synchronized { id4 += 1 id4 } l4 // Now we are sure that this will always be 10. l4.distinct.size // * assert(l4.distinct.size == 10)