Подтвердить что ты не робот

Как работает malloc в многопоточной среде?

Является ли типичный malloc (для платформы x86-64 и ОС Linux) наивно блокировать мьютексы в начале и выпускать его по завершении или блокирует мьютекс более умным способом на более тонком уровне, так что ограничение конкуренции сокращено? Если он действительно делает это вторым способом, как он это делает?

4b9b3361

Ответ 1

glibc 2.15 управляет несколькими аренами выделения. Каждая арена имеет свой замок. Когда поток должен выделять память, malloc() выбирает арену, блокирует ее и выделяет из нее память.

Механизм выбора арены несколько сложный и направлен на сокращение блокировки:

/* arena_get() acquires an arena and locks the corresponding mutex.
   First, try the one last locked successfully by this thread.  (This
   is the common case and handled with a macro for speed.)  Then, loop
   once over the circularly linked list of arenas.  If no arena is
   readily available, create a new one.  In this latter case, `size'
   is just a hint as to how much memory will be required immediately
   in the new arena. */

Имея это в виду, malloc() в основном выглядит так (отредактировано для краткости):

  mstate ar_ptr;
  void *victim;

  arena_lookup(ar_ptr);
  arena_lock(ar_ptr, bytes);
  if(!ar_ptr)
    return 0;
  victim = _int_malloc(ar_ptr, bytes);
  if(!victim) {
    /* Maybe the failure is due to running out of mmapped areas. */
    if(ar_ptr != &main_arena) {
      (void)mutex_unlock(&ar_ptr->mutex);
      ar_ptr = &main_arena;
      (void)mutex_lock(&ar_ptr->mutex);
      victim = _int_malloc(ar_ptr, bytes);
      (void)mutex_unlock(&ar_ptr->mutex);
    } else {
      /* ... or sbrk() has failed and there is still a chance to mmap() */
      ar_ptr = arena_get2(ar_ptr->next ? ar_ptr : 0, bytes);
      (void)mutex_unlock(&main_arena.mutex);
      if(ar_ptr) {
        victim = _int_malloc(ar_ptr, bytes);
        (void)mutex_unlock(&ar_ptr->mutex);
      }
    }
  } else
    (void)mutex_unlock(&ar_ptr->mutex);

  return victim;

Этот распределитель называется ptmalloc. Он основан на более ранней работе Дуга Ли и поддерживается Вольфрамом Глогером.

Ответ 2

Doug Lea malloc использовал грубую блокировку (или отсутствие блокировки в зависимости от настроек конфигурации), где каждый вызов malloc/realloc/free защищен глобальным мьютезом. Это безопасно, но может быть неэффективным в многопоточных средах.

ptmalloc3, который является реализацией по умолчанию malloc в библиотеке GNU C (libc), используемой в большинстве систем Linux в эти дни, имеет более тонкую стратегию, как описано в aix answer, которая позволяет нескольким потокам одновременно безопасно распределять память.

nedmalloc - еще одна независимая реализация, которая утверждает, что даже лучше, чем ptmalloc3, и много других распределителей. Я не знаю, как это работает, и, похоже, нет никакой очевидной документации, поэтому вам нужно будет проверить исходный код, чтобы увидеть, как он работает.