[Concurrency] volatile ν‚€μ›Œλ“œ (Java, C#)

volatile은 Java, C# λ“±μ—μ„œ λ™μ‹œμ„± μ½”λ“œλ₯Ό μž‘μ„±ν•  λ•Œ μ‚¬μš©λ˜λŠ” μ£Όμš” ν‚€μ›Œλ“œ 쀑 ν•˜λ‚˜λ‹€.

C/C++에도 volatile ν‚€μ›Œλ“œκ°€ μžˆμ§€λ§Œ, μš©λ„λ‚˜ 원리가 λ‹¬λΌμ„œ μ£Όμš” λ™μ‹œμ„± κ΅¬ν˜„ μˆ˜λ‹¨μœΌλ‘œ μ‚¬μš©λ˜μ§€ μ•ŠλŠ”λ‹€λŠ” 것을 μœ μ˜ν•œλ‹€. ν˜Όλ™ν•˜μ§€ μ•Šμ•„μ•Ό ν•œλ‹€.

그리고 κ½€ λ§Žμ€ μ‚¬λžŒλ“€μ΄ μ°©κ°ν•˜λŠ”κ²Œ μžˆλŠ”λ°, 이건 μ§μ ‘μ μœΌλ‘œ μŠ€λ ˆλ“œμ„Έμ΄ν”„λ₯Ό μ œκ³΅ν•˜λŠ”κ²Œ μ•„λ‹ˆλΌ μŠ€λ ˆλ“œμ„Έμ΄ν”„λ₯Ό κ΅¬ν˜„ν•  수 μžˆλŠ” 쑰건 쀑 ν•˜λ‚˜λ₯Ό μ œκ³΅ν•˜λŠ” 것이닀.

μ—¬κΈ°μ„œλŠ” Javaλ₯Ό κΈ°μ€€μœΌλ‘œ volatile이 λ™μž‘ν•˜λŠ” 원리와 μŠ€λ ˆλ“œ κ°„ 동기화λ₯Ό κ΅¬ν˜„ν•˜λŠ” 방법을 닀뀄본닀.




이게 μ •ν™•νžˆ 무엇인가?

사싀 이건 μŠ€λ ˆλ“œμ™€ 직접적인 연관이 μžˆλŠ” μš”μ†ŒλŠ” μ•„λ‹ˆλ‹€.
ν•΄λ‹Ή λ³€μˆ˜μ˜ 값을 μ½μ„λ•Œ 항상 "λ©”λͺ¨λ¦¬(RAM)"μ—μ„œ μ½μ–΄μ˜€κ³ , μˆœμ„œλŒ€λ‘œ μ—°μ‚°ν•˜λ„λ‘ κ°•μ œν•˜λŠ” ν‚€μ›Œλ“œλ‹€.

volatile ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠμœΌλ©΄, μ»΄νŒŒμΌλŸ¬λŠ” λ³€μˆ˜λ₯Ό μ½μ„λ•Œ μΊμ‹œμ—μ„œ μ½μ–΄μ˜¬ 수 μžˆλŠ” ν˜•νƒœλ‘œ μ½”λ“œλ₯Ό μƒμ„±ν•œλ‹€.

https://nesoy.github.io/articles/2018-06/Java-volatile
κ·Έ μ΄μœ λŠ” λ‹Ήμ—°νžˆ λΉ λ₯΄κΈ° λ•Œλ¬Έμ΄λ‹€.

이게 μŠ€λ ˆλ“œ κ°„ 동기화λ₯Ό λ§μΉ˜λŠ” μ£Όλ²” 쀑 ν•˜λ‚˜λ‹€.
값을 λ³€κ²½ν•˜λ”λΌλ„ 그게 μΊμ‹œμ— λ°˜μ˜λ˜λŠ” λ”œλ ˆμ΄ λ•Œλ¬Έμ— λ‹€λ₯Έ μŠ€λ ˆλ“œμ—μ„œ 잘λͺ»λœ μ˜› 값을 읽을 수 있기 λ•Œλ¬Έμ΄λ‹€.
volatile은 μ΄λŸ¬ν•œ μ΅œμ ν™”λ₯Ό μ œκ±°ν•œλ‹€.

κ²Œλ‹€κ°€ μ»΄νŒŒμΌλŸ¬λŠ” μ΅œμ ν™” κ³Όμ • 쀑에 Reorderingμ΄λΌλŠ” 과정을 μˆ˜ν–‰ν•˜κ³€ ν•œλ‹€.
μ½”λ“œμ—μ„œ μž‘μ„±ν•œ μ—°μ‚° μˆœμ„œλ₯Ό λ©‹λŒ€λ‘œ 뒀집을 수 μžˆλ‹€λŠ” 것이닀.

https://hyojun.tistory.com/entry/LeetCode-143-Reorder-List-Java
이거도 μ„±λŠ₯ μ΅œμ ν™”λ₯Ό μœ„ν•΄μ„œ μˆ˜ν–‰λ˜λŠ”λ°, μ‹±κΈ€μŠ€λ ˆλ“œμ—μ„œλŠ” λ¬Έμ œμ—†μ§€λ§Œ λ©€ν‹°μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œλŠ” 잘λͺ»λœ μ—°μ‚° κ²°κ³Όλ₯Ό μ΄ˆλž˜ν•  수 μžˆλ‹€.

κ·Έλž˜μ„œ volatile은 이런 reordering이 λ°œμƒν•˜μ§€ μ•Šλ„λ‘ λ°©μ§€ν•œλ‹€.




μš©λ„

μ•„λ¬΄νŠΌ μœ„μ—μ„œ μ–ΈκΈ‰ν•œ μ΅œμ ν™”λ“€μ„ λ°©μ§€ν•˜κΈ° λ•Œλ¬Έμ—, volatile둜 μ„ μ–Έλœ λ³€μˆ˜λŠ” λ©€ν‹°μŠ€λ ˆλ“œ κ³΅μœ μ™€ κ΄€λ ¨ν•΄μ„œ 보μž₯을 μ’€ κ°€μ§ˆ 수 μžˆλ‹€.

항상 μ΅œμ‹ μ˜ 값을 μ½μ–΄μ˜€κ²Œ ν•˜κΈ° λ•Œλ¬Έμ— λͺ¨λ“  μŠ€λ ˆλ“œκ°€ μ΅œμ‹  값을 μ‹€μ‹œκ°„μœΌλ‘œ μ°Έμ‘°ν•˜κ²Œ ν•  수 μžˆλ‹€λŠ” 것이닀.

κ·Έλž˜μ„œ volatile λ³€μˆ˜λŠ” ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œκ°€ writeλ₯Ό ν•˜κ³ , N개의 μŠ€λ ˆλ“œκ°€ readλ₯Ό ν•˜λŠ” μƒν™©μ—μ„œλŠ” μ•ˆμ „ν•˜κ²Œ μ‚¬μš©ν•  수 μžˆλ‹€.
ν•˜μ§€λ§Œ λ™μ‹œμ— μ—¬λŸ¬κ°œμ˜ threadκ°€ writeλ₯Ό μ‹œλ„ν•  수 μžˆλŠ” 상황에 λŒ€ν•΄μ„œλŠ” λŒ€μ‘ν•˜μ§€ λͺ»ν•¨μ„ μœ μ˜ν•œλ‹€.




예제: κ°€μ§œ Lock λ§Œλ“€μ–΄λ³΄κΈ°

직접 μ½”λ“œλ₯Ό μž‘μ„±ν•΄μ„œ volatile이 μ–΄λ–»κ²Œ λ™μž‘ν•˜λŠ”μ§€ μ•Œμ•„λ³΄κ² λ‹€.
μ•„λž˜λŠ” μ•„λ¬΄λŸ° 동기화 μˆ˜λ‹¨λ„ 없이 Lock을 κ΅¬ν˜„ν•œ κ°„λ‹¨ν•œ ν΄λž˜μŠ€λ‹€.

κ·Έλƒ₯ lock boolean κ°’ ν•˜λ‚˜ κ°€μ Έλ‹€κ°€ 기닀리고 λ“€μ–΄κ°€κ³  ν•˜λŠ” κ°„λ‹¨ν•œ μ½”λ“œλ§Œ μž‘μ„±ν•΄λ†¨λ‹€.


그리고 μ €κ±Έ κ°€μ Έλ‹€κ°€ 동기화 λΉ„μŠ€λ¬΄λ¦¬ν•˜κ²Œ ν•˜λ©΄μ„œ 곡유 λ³€μˆ˜μ— μ ‘κ·Όν•˜λ € ν•˜λ©΄


맀우 높은 ν™•λ₯ λ‘œ λ°λ“œλ½μ— κ±Έλ €μ„œ ν”„λ‘œκ·Έλž¨μ΄ 쀑단될 것이닀.
μœ„μ— μ–ΈκΈ‰ν–ˆλ˜ μ΄μœ λ“€λ‘œ μΈν•΄μ„œ μŠ€λ ˆλ“œ μΊμ‹œκ°€ 남고, μˆœμ„œκ°€ 꼬이기 λ•Œλ¬Έμ΄λ‹€.

이제 volatile둜 λ°”κΏ”λ³΄μž.

그러면 이제 μˆ˜μ •μ‚¬ν•­μ΄ λ‹€λ₯Έ μŠ€λ ˆλ“œμ—μ„œμ˜ μ ‘κ·Όμ—μ„œλ„ λ°”λ‘œ 반영되기 λ•Œλ¬Έμ—, λ°λ“œλ½μ΄ λ°œμƒν•˜λŠ” 문제 μžμ²΄λŠ” 해결이 λœλ‹€.

λ‹€λ§Œ 이걸둜 μ œλŒ€λ‘œ 된 락을 λ§Œλ“€ μˆ˜λŠ” μ—†κΈ° λ•Œλ¬Έμ— 크게 μ˜λ―ΈμžˆλŠ” μ½”λ“œλŠ” μ•„λ‹ˆκΈ΄ ν•˜λ‹€.
κΈ°λŒ€κ°’μΈ 50000보닀 훨씬 μž‘μ€ 값이 λ‚˜μ˜¨κ±΄, μ—¬λŸ¬ μŠ€λ ˆλ“œκ°€ λ™μ‹œμ— writeλ₯Ό ν•˜λ©΄μ„œ λ™μ‹œ 점유λ₯Ό ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.
μœ„μ—μ„œ μ–ΈκΈ‰ν–ˆλ“―μ΄ volatile은 1개의 μŠ€λ ˆλ“œλ§Œμ΄ writeλ₯Ό μ‹œλ„ν• λ•Œ μœ νš¨ν•˜κ²Œ μ‚¬μš©ν•  수 μžˆλŠ” κΈ°λŠ₯이닀.

Lock 같은 κ°•λ ₯ν•œ λ™μ‹œμ„±μ„ κ΅¬ν˜„ν•  λ•ŒλŠ” μ‚¬μš©ν•  수 μ—†μ§€λ§Œ, ν•œ μŠ€λ ˆλ“œκ°€ μž‘μ—…μ„ 끝낸 뒀에 κ²°κ³Όλ₯Ό λ‹€λ₯Έ μŠ€λ ˆλ“œμ— λ°”λ‘œλ°”λ‘œ 톡지할 ν•„μš”κ°€ μžˆμ„ λ•Œ μœ μš©ν•œ κΈ°λŠ₯이닀.


예제 μ½”λ“œ

/*
 * This source file was generated by the Gradle 'init' task
 */
package app.src.main.java.org.example;

import java.util.ArrayList;

class FakeLock {
    boolean locked = false;

    public void lock() {
        while (locked) {
            // wait...
        }
        locked = true;
    }

    public void unlock() {
        locked = false;
    }
}

public class App {
    static int sharedCounter = 0;

    public static void main(String[] args) {
        FakeLock lock = new FakeLock();

        System.out.println("Starting threads...");

        var threads = new ArrayList<Thread>();
        for (int i = 0; i < 5; i++) {
            var thread = new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    lock.lock();
                    sharedCounter++;
                    lock.unlock();
                }
            });
            threads.add(thread);
            thread.start();
        }

        for (var thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("Counter value: " + sharedCounter);
    }
}

μ°Έμ‘°
https://stackoverflow.com/questions/106591/what-is-the-volatile-keyword-useful-for