1

Тема: LUA Threads Tutorial

Потоки в LUA. Цикл статей.

Многозадачность, потоки и LUA

  ANSI C, стандарт, к которому Lua соответствует, не имеет механизма для управления несколькими потоками исполнения. Потоки и объекты синхронизации, используемые для управления ими обеспечиваются операционной системой. Вам следует использовать именно их в реализации своих потоков в Lua. Вам не нужно будет изменять дистибутив Lua, чтобы сделать это.

  Управление потоками не входит в число простых задач даже если вы программируете на чистом C. Приложение, которое встраивается или расширяет функциональность Lua должно столкнуться с дополнительной сложностью координации потоков с библиотекой Lua. Если ваши потребности в многозадачности могут быть удовлетворены с помощью однопоточных сопрограмм Lua (coroutines), лучшим советом для вас можно было назвать выбрать именно этот маршрут. Читайте CoroutinesTutorial для более подробной информации.  Если вы всё же решитесь пойти по пути реализации истинной многопоточности в своём Lua-проекте, следующие рекомендации могут помочь.

  Каждый поток в C, который взаимодействует с Lua нуждается в собственном LUA-state (состоянии или контексте LUA-машины). Каждое из этих состояний (контекстов) имеет свой собственный стек выполнения. Когда новый C-поток запускается, вы можете создать своё Lua-состояние одним из двух способов.

    Один из способов заключается в вызове lua_open . Это создает новое lua-состояние, которое не зависит от состояния в других потоках. В этом случае, вам нужно инициализировать состояние Lua (например, загрузка библиотек), как если бы это было состояние новой программы. Такой подход исключает необходимость в объектах синхронизации и блокировок, например мутексах (mutex) (обсуждается ниже), но исключает возможность корректно организовать межпоточный обмен с глобальными данными.

    Другой подход заключается в вызове lua_newthread . Таким образом мы создаём дочернее lua-состояние (child state), котороё имеет свой собственный стек и которое имеет доступ к глобальным данным. Этот подход обсуждается здесь.


Locking by the LUA

Ваше самое большое беспокойство при работе с потоками - это предотвратить нежелательное изменение в контексте общих данных и обеспечение корректного доступа. Те или иные проблемы постоянно возникают когда два потока взаимодействуют друг с другом: один уже перешёл в другое состояние, а другой всё ещё ожидает и т. п. Однако, обычно, ваши потоки имеют определенные структуры данных. И тут, объекты-семафоры из операционной системы вступают в игру. Объект этого типа может быть заблокирован с помощью не более чем одного потока одновременно.

  Сама LUA немного помогает в защите внутренних структур данных от повреждения. Когда Lua исполняет код, который не должен иметь доступ из других потоков, она вызывает lua_lock . Когда критическая операция будет завершена, она вызывает lua_unlock .  В стандартном дистрибутиве эти две функции ничего не делают. При использовании потоков в Lua, они должны быть заменены на функции в зависимости от операционной системы:

    В среде POSIX вы будете использовать объект типа pthread_mutex_t .

    В Windows, вы будете использовать либо ручку, которая вернулась из CreateMutex , или, что более оптимально, непрозрачной структурой данных типа CRITICAL_SECTION .

Все сопрограммы из одного Lua-сотояния должны использовать одинаковый мьютекс. Избегайте ошибку, связывая мьютекс с конкретным контекстом Lua-state, а затем, не найдя его, когда разные сопрограммы в том же контексте блокируются. Простой пример для Win32. Файл пользовательского заголовка luauser.h может содержать:

   #define lua_lock (L) LuaLock (L)
   #define lua_unlock (L) LuaUnlock (L)
   #define lua_userstateopen (L) LuaLockInitial (L)
   #define lua_userstatethread (L, L1) LuaLockInitial (L1) // Lua 5.1

   void LuaLockInitial (lua_State * L);
   void LuaLockFinal (lua_State * L);
   void LuaLock (lua_State * L);
   void LuaUnlock (lua_State * L);

Все три определения препроцессора будет использованы при компиляции Lua. Начиная с версии 5.0.2, Lua, к сожалению, не обеспечивает вызов destroy lock. ( VersionNotice : обновление для Lua 5.1?)
Функция lua_userstateopen будет вызываться всякий раз, когда новое состояние Lua будет создано, не важно вызвано оно lua_open или lua_newthread . Важно, чтобы мьютекс должен быть создан только один раз когда lua_userstateopen будет вызвано.

В Lua 5.1 luai_userstatethread(L,L1) вызывается для потоков, созданных с lua_newthread  и luai_userstateopen(L) вызывается для Lua-состояний, созданных lua_newstate (но не lua_newthread ). luai_userstateclose(L) вызывается только для потоков закрытых lua_close .

Ниже, файл luauser.c к описанному выше заголовку:

 #include <windows.h>
  #include "lua.h"
  #include "luauser.h"

  static struct {
    CRITICAL_SECTION LockSct;
    BOOL Init;
  } Gl;

  void LuaLockInitial(lua_State * L) 
  { 
    if (! Gl.Init) 
    {
      /* Create a mutex */
      InitializeCriticalSection(&Gl.LockSct);
      Gl.Init = TRUE;
    }
  }

  void LuaLockFinal(lua_State * L) /* Not called by Lua. */
  { 
    /* Destroy a mutex. */
    if (Gl.Init)
    {
      DeleteCriticalSection(&Gl.LockSct);
      Gl.Init = FALSE;
    }
  }

  void LuaLock(lua_State * L)
  {
    /* Wait for control of mutex */
    EnterCriticalSection(&Gl.LockSct);
  }

  void LuaUnlock(lua_State * L)
  { 
    /* Release control of mutex */
    LeaveCriticalSection(&Gl.LockSct);
  }

Нет нужды включать эти 2 файла в дистрибутив но, они должны находиться в сборке. Кроме того, вы должны будете подправить определение LUA_USER.H,  что-то вроде

      /DLUA_USER_H="""luauser.h "" "

также возможно потребуется изменить include path, чтоб компилятор мог найти ваш файл.

Locking by the application

Lua предотвращает разрушение внутренних структур данных с помощью функций запирания. Мьютекс может быть использован для координации использования ресурсов с помощью таких функций, как те, что показаны выше. Тем не менее, не забудьте использовать различные мьютексы, чем тот, который используется Lua для того, чтобы избежать возможных тупиков.
При проектировании многопоточных приложений, полезно обратить особое внимание туда, где каждый поток ждет. Всегда быть в курсе, что неосторожные действия могут быть прерваны. Если какой-либо другой поток может оказать негативное воздействие на состояние, в котором происходит прерывание, вызываются некоторые формы взаимного исключения.

Global mutex and multiprocessing

Описанный выше метод для реализации многопоточности в Lua с использованием глобального мьютекса неэффективен на многопроцессорных системах.  В то время, пока один поток удерживает глобальный мьютекс, другие потоки ждут его. Таким образом, только один поток Lua может быть запущен в одно и тоже время, независимо от количества процессоров в системе.

В некоторых системах может быть необходимо передать управление другому потоку после разблокировки мьютекса, чтобы предотвратить от блокировки потока снова, в случае, если есть другие потоки ждут очереди. Это происходит по крайней мере на Linux, используя Boost.Threads. Перекрытие luai_threadyield (которая вызывает по умолчанию lua_unlock , а затем сразу же lua_lock ), вероятно, хорошая идея. Тем не менее, luai_threadyield вызывается макросом dojump в виртуальной машине, следовательно, выход на каждом вызове luai_threadyield может значительно снизить производительность. Следующая альтернатива может быть полезна:

void luai_threadyield(struct lua_State *L)
{
    static int count=0;
    bool y=false;
    if (count--<=0) { y=true; count=30; }; // Try different values instead of 30.
    lua_unlock(L);
    if (y) thread::yield();
    lua_lock(L);
}

Оригинал на англ.: http://lua-users.org/wiki/ThreadsTutorial
Переведено: sam063rus
за что ему большое спасибо! admin

2 (2016-09-29 11:57:07 отредактировано sam063rus)

Re: LUA Threads Tutorial

-

3 (2016-09-29 11:24:36 отредактировано sam063rus)

Re: LUA Threads Tutorial

-

4 (2016-09-29 11:55:11 отредактировано sam063rus)

Re: LUA Threads Tutorial

-

5 (2016-09-29 11:44:20 отредактировано sam063rus)

Re: LUA Threads Tutorial

-

6

Re: LUA Threads Tutorial

sam063rus пишет:

Библиотека поддержки настоящей многопоточности и мултипроцессорности в LUA: https://github.com/LuaLanes/lanes

Очень жаль, что разработчики QLUA "забили" на неё.

Таких библиотек довольно много. Главное неудобство - отдельный стейт на каждый "поток".

7 (2016-09-29 11:44:12 отредактировано sam063rus)

Re: LUA Threads Tutorial

-

8

Re: LUA Threads Tutorial

sam063rus пишет:

можно подумать, сейчас у Вас не отдельный стейт на каждый скрипт?
главное удобство - нативная поддержка shared data между двумя стейтами с помощью linda-объектов. Или я, что-то не так понял??

На каждый скрипт - да, но там отдельный стейт на каждый поток. Это как раз причина почему для общих данных ( shared data ) используются вот такие хитрые способы типа linda-объектов.

9 (2016-09-29 11:44:05 отредактировано sam063rus)

Re: LUA Threads Tutorial

-

10 (2016-09-29 11:43:57 отредактировано sam063rus)

Re: LUA Threads Tutorial

-

11

Re: LUA Threads Tutorial

sam063rus пишет:

На каждый скрипт - да, но там отдельный стейт на каждый поток

мне наверно следовало дописать так:
можно подумать, сейчас у Вас не отдельный стейт на каждый скрипт (а значит, благодаря майну в QLUA - и поток)? Так чем же Ваше решение является более оригинальным?
На мой взгляд, если бы Вы сейчас интегрировали в QLUA (ну хотя б после того как выйдете на более-менее стабильный релиз) lualanes - то, QLUA - перешла бы на совершенно иной качественный уровень. И пользователи были освобождены от львиной доли головной боли с синхронизацией - бо как это уже во многом реализовано нативно в lualanes.
Может я ошибаюсь, конечно - готов это обсуждать.

Не было задачи предложить оригинальное решение, пользовались тем что позволяет сделать Lua.
А насчет интеграции - можете сейчас использовать lanes, никто же не запрещает. В этом случае я с Вами согласен - проблемы с синхронизацией у нас бы пропали и перешли на сторону пользователя.

12 (2016-09-29 11:43:44 отредактировано sam063rus)

Re: LUA Threads Tutorial

-

13 (2016-09-29 11:43:30 отредактировано sam063rus)

Re: LUA Threads Tutorial

-

14

Re: LUA Threads Tutorial

По поводу GUI есть планы, сроки пока не определены.
По поводу многоядерности и QLUA - поясните подробнее что хочется? Серебряных пуль в мире практически не существует. Можно выиграть в одном и проиграть в другом.

15 (2016-09-29 11:43:23 отредактировано sam063rus)

Re: LUA Threads Tutorial

-

16

Re: LUA Threads Tutorial

Давайте ссылки где можно скачать, посмотрим.
Намеков не надо, хочется понимать собеседника однозначно. Вам же никто не ставит ТЗ "Ну ты меня понял, да?"
Давайте я тоже намекну - судя по тому что Вы собираете у Вас  есть желание на основе терминала построить систему третьего уровня? Это я так, для общего понимания Ваших задач, могу  ошибаться. Я просто не понимаю что дает многопоточная отправка транзакций.

17 (2016-09-29 11:43:08 отредактировано sam063rus)

Re: LUA Threads Tutorial

-

18 (2016-09-29 11:43:00 отредактировано sam063rus)

Re: LUA Threads Tutorial

-

19

Re: LUA Threads Tutorial

sam063rus пишет:

At this time - это можно сделать только без применения GUI-библиотек. Что весьма плохо.

GUI добавляет стабильности?

20 (2016-09-29 11:42:52 отредактировано sam063rus)

Re: LUA Threads Tutorial

-

21 (2015-09-29 11:53:35 отредактировано sam063rus)

Re: LUA Threads Tutorial

-