← Назад к вопросам

Приведи пример GIL в других языках программирования

2.3 Middle🔥 131 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

GIL в других языках программирования

Global Interpreter Lock — концепция не уникальна для Python. Другие интерпретируемые языки сталкивались с похожими проблемами или специально избегали их.

GIL-подобные механизмы в других языках

Ruby: Global VM Lock (GVL)

Ruby имеет почти идентичный механизм — Global VM Lock, часто называемый Ruby's GIL:

# Ruby использует один глобальный lock для thread safety
threads = []

5.times do
  threads << Thread.new do
    # Только один thread может выполнять Ruby код одновременно
    1_000_000.times { |i| i += 1 }
  end
end

threads.each(&:join)

# На многоядерной системе это медленнее, чем одноядерный вариант
# Потому что threads конкурируют за GVL

Результат:

  • Однопоточный код: ~100ms
  • 5 threads: ~300ms (медленнее!)

PHP: No threading at all

PHP исторически никогда не имел правильной поддержки многопоточности:

<?php
// PHP просто не допускает потоки
// Вместо этого используется multiprocessing через separate processes

// Только в PHP 7.4+ появилась хорошая поддержка
// Но даже тогда это не популярно

// Вместо threading используют:
// 1. Apache/nginx workers (separate processes)
// 2. Background jobs (Redis queues, RabbitMQ)
// 3. Async PHP (ReactPHP, Amphp) — но это не threading
?>

Java: НЕ имеет GIL

Вот ключевое отличие — Java специально спроектирована БЕЗ GIL:

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        // Java МОЖЕТ запускать потоки параллельно на разных ядрах
        Thread[] threads = new Thread[8];
        
        for (int i = 0; i < 8; i++) {
            threads[i] = new Thread(() -> {
                // Это ДЕЙСТВИТЕЛЬНО выполняется параллельно
                // на 8 разных ядрах процессора
                long sum = 0;
                for (int j = 0; j < 1_000_000_000; j++) {
                    sum += j;
                }
                System.out.println(sum);
            });
            threads[i].start();
        }
        
        for (Thread t : threads) {
            t.join();
        }
    }
}

Результат на 8-ядерной машине:

  • Однопоточный: ~8 seconds
  • 8 threads: ~1 second (8x ускорение!)

Почему Java не имеет GIL?

  • Используется true sharing объектов между потоками
  • Использует fine-grained locking (lock на объект, не глобальный)
  • Разработчики должны управлять синхронизацией (synchronized, volatile)

Go: Goroutines без GIL

Go специально избежал GIL через горутины:

package main

import (
    "fmt"
    "sync"
)

func main() {
    // Go может запускать тысячи goroutines параллельно
    var wg sync.WaitGroup
    
    for i := 0; i < 10000; i++ {
        wg.Add(1)
        go func(id int) {
            // Миллионы горутин могут выполняться на разных ядрах
            // без GIL
            defer wg.Done()
            println("Goroutine", id)
        }(i)
    }
    
    wg.Wait()
}

Почему Go избежал GIL?

  • Горутины — это не OS threads, это lightweight
  • Go runtime распределяет их по worker threads
  • Shared memory управляется channels (безопаснее)

Rust: Нет GIL, есть borrow checker

Rust полностью избежал GIL и thread-safety проблем:

use std::thread;

fn main() {
    let mut handles = vec![];
    
    for i in 0..8 {
        let handle = thread::spawn(move || {
            // Rust гарантирует thread safety ЧЕРЕЗ компилятор
            // Нет GIL, потому что нет shared mutable state
            let sum: u64 = (0..1_000_000_000).sum();
            println!("Thread {}: {}", i, sum);
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
}

Почему Rust избежал проблемы?

  • Borrow checker предотвращает undefined behavior на compile time
  • Нет GIL, потому что это не нужно
  • Shared mutable state проверяется компилятором

JavaScript: Single-threaded, async/await

JavaScript решил проблему иначе — избежал многопоточности:

// JavaScript это однопоточный язык
// Нет потоков, есть event loop

async function main() {
    // Это выглядит как параллельное выполнение
    // Но на самом деле это async/await в single thread
    
    const promise1 = fetch('/api/1').then(r => r.json());
    const promise2 = fetch('/api/2').then(r => r.json());
    
    const [data1, data2] = await Promise.all([promise1, promise2]);
    console.log(data1, data2);
}

// Web Workers для настоящей параллельности
const worker = new Worker('worker.js');
worker.postMessage({task: 'compute', data: largeArray});
worker.onmessage = (e) => console.log(e.data);

Это решение для I/O-bound задач, но для CPU-bound нужны Web Workers (отдельные потоки).

Сравнение подходов

ЯзыкМеханизмПараллельностьДля кого
PythonGILI/O onlyScripting, data science
RubyGVLI/O onlyRails development
JavaMonitors (объектные locks)НастоящаяBackend, enterprise
GoGoroutinesНастоящаяСистемное ПО, concurrent services
RustBorrow checkerНастоящаяСистемное ПО, performance
C#no GILНастоящая.NET applications
C/C++нет ничегоНастоящаяСистемное ПО, performance
JavaScriptEvent loopI/O only (или Web Workers)Frontend, Node.js

Почему Python выбрал GIL

Это историческое решение 1990х:

  1. Простота реализации — один глобальный lock проще, чем fine-grained locking
  2. Производительность однопоточного кода — GIL позволяет быстро работать с памятью
  3. Compatibility с C extensions — многие расширения не потокобезопасны

Как обойти GIL в Python

# 1. Multiprocessing (отдельные процессы)
from multiprocessing import Pool

with Pool(4) as p:
    results = p.map(cpu_bound_func, data)

# 2. Asyncio для I/O (не требует потоков)
import asyncio

async def fetch_many(urls):
    tasks = [fetch(url) for url in urls]
    return await asyncio.gather(*tasks)

# 3. Numpy/C расширения (отпускают GIL)
import numpy as np
result = np.dot(matrix1, matrix2)  # Не держит GIL

# 4. Cython/Numba для компиляции
from numba import jit

@jit(nopython=True)  # Компилируется в машинный код
def fast_compute(data):
    # Выполняется без GIL
    return sum(data)

Вывод

  • GIL уникален для интерпретируемых языков (Python, Ruby) с динамической типизацией
  • Язык выбирает: GIL (простота) или правильная многопоточность (сложность)
  • Java/Go/Rust решили иначе и получили настоящую параллельность
  • Python исторически выбрал GIL, что хорошо для I/O и плохо для CPU-bound

Для CPU-bound задач в Python используйте multiprocessing, для I/O используйте asyncio.

Приведи пример GIL в других языках программирования | PrepBro