51 (2021-06-10 16:04:51 отредактировано toxa)

Re: библиотека lua_share (обмен данными между скриптами lua)

Андрей_ пишет:

Но синхронизовать их все равно нужно по взрослому.

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

если вам нужно, чтобы вся процедура обработки данных так же была защищена, вы пишите код в lua_share_boot.lua и вызываете его, либо напрямую: через rpc, либо косвенно: при доступе к данным через метаметоды.

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

52

Re: библиотека lua_share (обмен данными между скриптами lua)

kalikazandr пишет:
Андрей_ пишет:

Да нет, смысл как раз в том, чтобы каждый скрипт\поток получал целостную версию совокупных данных и так же передавал ее дальше. Друг другу. А обработка как в критической секции, по очереди. Это делается мьютексом. Если бы каждый мог менять свою часть данных независимо, это была бы уже задача с N объектами данных (частей). А дальше та же подзадача синхронизации для каждой части ) Да в принципе мьютексов то бояться не надо. Пусть мы не можем создавать потоки сами, но мы можем делать это, создавая макросы с их потоками main. Но синхронизовать их все равно нужно по взрослому.

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

Для "синхронизации" в lua_share существует специальное пространство имен:
- predefined "queues" namespace implementation.
Это решает вашу задачу на 100%, а вот как долго она будет решаться? напрямую зависит от кол-ва скриптов х на 1-15 мс
А оно вам надо - такая многопоточность?

Можете на словах описать как queue обеспечивает синхронизацию доступа к некоему объекту данных? Схема должна содержать 2 действия скрипта - взять объект эксклюзивно, отпустить.

53 (2021-06-10 20:46:24 отредактировано toxa)

Re: библиотека lua_share (обмен данными между скриптами lua)

для этого не нужен объект queue. смотрите как это работает:

    ns = sh.GetNameSpace("additional_name_space")

"взять объект эксклюзивно, записать, отпустить":

    ns["test"] = {["foo"] = "bar", [123] = 456, {1,2,3}, {4,5,6}, [{1,2,3}] = "tblkey"}

если вам нужно эксклюзивно что-то обработать, то есть выполнить несколько связанных действий, чтобы вас не могли прервать, то либо вы  используете rpc:

    s = sh.GetIPCNameSpace("test_name_space")
    local a, b, c = ns("testfunc", "a", {1, 2, {3, "b"}}) -- testfunc("a", {1, 2, {3, "b"}})

либо делаете некий объект (таблицу), и в ее метаметодах пишете код. например, есть та же queue. когда вы присваиваете значения таблице queue:

    ns = sh.GetNameSpace("queues") -- get predefined "queues" namespace
    ns["queue1"] = 100

на самом деле вызывается метаметод, который помещает значение 100 в очередь с именем queue1. выполнение этого кода атомарно с точки зрения вызывающего скрипта. хотя внутри там довольно хитрый lua-код, можно посмотреть в lua_share_boot.lua:

function(self, key, value)
        local idx = nil
        if type(key)~="table" then
            idx = key
        else
            idx = __findkey(self.__data, key)
            if idx == nil then idx = key end
        end
        local queue = self.__data[idx]
        if queue == nil then
            queue = self.__new()
            self.__data[idx] = queue
        end;
        self.__push(queue, value)
    end

и это не все, потому что там еще определены __findkey(), __push() и все прочее. то есть, происходит "взять объект, исполнить некий код с ним и дополнительными аргументами, отпустить объект".

54

Re: библиотека lua_share (обмен данными между скриптами lua)

toxa пишет:

если вам нужно, чтобы вся процедура обработки данных так же была защищена, вы пишите код в lua_share_boot.lua и вызываете его, либо напрямую: через rpc, либо косвенно: при доступе к данным через метаметоды.

Тема с lua_share_boot.lua как-то не достаточно раскрыта. Мне надо определять свои структуры данных в ней, чтобы доступ к ним синхронизировался? Нужно понимать принципы, это не описано. Например, какой код там написать для простой задачи. Есть некая таблица. Нужно уметь обрабатывать ее целостно и эксклюзивно разными потоками. И чтобы потоки ожидали высвобождения ее другими. Алгоритмы ее обработки естественно включают кучу внешних данных и заложены в макросах.

Я пока не понимаю как это возможно, но уверен, что в любом случае явные методы get_exclusive (блокирующий) и free для произвольного объекта общей памяти были бы удобней. Так есть там мьютекс под объектом или нет?

55 (2021-06-10 20:58:28 отредактировано Андрей_)

Re: библиотека lua_share (обмен данными между скриптами lua)

Значит эксклюзивная обработка поддерживается только в отдельном потоке (сейчас это екзешник) и нужно умудриться всю логику описать в этой testfunc, пакуя ей все необходимые данные? И потоки, вызвавшие ее до ее окончания другим потоком, ждут, просыпаются по очереди и т.д.?

А теперь внимание. Поток не может сделать вызов этой функции с нужными параметрами, т.к. сначала должен посмотреть текущее состояние объекта после его изменений другим. Это и определит обработку. И вот это "взять, посмотреть, выбрать алгоритм, обработать, освободить" - должно быть атомарным, другие должны ждать.

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

56

Re: библиотека lua_share (обмен данными между скриптами lua)

"отдельный экзешник" нужен только для межпроцессного взаимодействия: когда вы шарите данные между несколькими разными квиками. если вам нужен общий доступ к объектам внутри одного квика (но из разных скриптов) то он не нужен, можно его не запускать. в самом квике тоже есть общее хранилище в котором загружен тот же lua_share_boot.lua

никаких дополнительных потоков в квике не запускается, исполнение общего кода, в этом случае, происходит в контексте треда того скрипта, который в данный момент производит вызов.

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

get()/free() хуже, потому что это  тяжело контролировать. что-нибудь где-нибудь не отпустили, или отпустили не в том порядке - и готово: threadlock. вы же не переживаете, что lua за вас память аллоцирует и освобождает. тут то же самое.

57

Re: библиотека lua_share (обмен данными между скриптами lua)

Андрей_ пишет:

Нужно уметь обрабатывать ее целостно и эксклюзивно разными потоками. И чтобы потоки ожидали высвобождения ее другими. Алгоритмы ее обработки естественно включают кучу внешних данных и заложены в макросах.

возьмите lua_share_boot.lua и посмотрите, как реализован объект queues. это "таблица, которая обрабатывается целостно и эксклюзивно разными потоками, и чтобы потоки ожидали ее высвобождения другими". то есть вот то, что вы написали.

58

Re: библиотека lua_share (обмен данными между скриптами lua)

пример testfunc - из скрипта сервера lua_share_server.lua, не lua_share_boot.lua. В нем и методов RPC нет, они тоже в скрипте сервера. Неясно как этот RPC осуществить без сервера. Как минимум надо описать, что именно нужно перенести в lua_share_boot.lua. И вы предлагали этот вариант как синхронизацию. Так все же если testfunc будет в  lua_share_boot.lua, то ее вызовы будут выстраиваться в очередь с ожиданием?

И все же да, логику обрабоки лучше не пихать в boot. Второй ваш пример - queues. Но и здесь в лучшем случае нужно будет всю логику обработки вложить в эти метаметоды, в boot. Тоже не хорошо. По мне так гораздо логичнее добавить поддержку управляемого мьютекса в объекте. Это стандарт многопоточного программирования де факто. Спасибо конечно за заботу о дедлоках, но такими драконовскими ограничениями.. ) нет уж, это через чур. Если все общие данные в одном объекте, то риск дедлоков минимален. И я думаю вы лукавите, говоря, что вышеописанное хоть как-то сравнимо по удобству с get()/free(). Ну не может же у нас быть НАСТОЛЬКО разная логика ) Да и реализовать это должно быть просто. Кто захочет - будет использовать. Кто боится - нет. Может добавите?

59

Re: библиотека lua_share (обмен данными между скриптами lua)

потому что я не знаю, какая у вас целевая архитектура: один у вас квик, пять квиков? один робот, пять роботов? я описываю  возможные варианты, их много: больше чем один.

нет, я не буду это добавлять. у библиотеки lua_share есть определенная идея: как это все должно взаимодействовать. сейчас при помощи этой библиотеки практически невозможно нарушить работу quik. если не делать этого специально, например, зациклив внутри lua_share_boot. это хорошее свойство, не хотелось бы его терять.

60

Re: библиотека lua_share (обмен данными между скриптами lua)

Так все же, если testfunc будет в  lua_share_boot.lua, то ее вызовы будут выстраиваться в очередь с ожиданием?

Я довольно наивно добавил в queues ф-цию 

function test(a)
        message(a..' started')
        sleep(5000)
        message(a..' ended')
    end

Планировал вызвать
local queues = sh.GetNameSpace("queues")
queues.test('a')

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

61

Re: библиотека lua_share (обмен данными между скриптами lua)

Теперь делаю эту ф-цию просто в корне lua_share_boot.lua
Как ее вызвать?

Касательно вашего примера
local ns = sh.GetIPCNameSpace("test_name_space")
local a, b, c = ns("testfunc", "a", {1, 2, {3, "b"}})
Почему test_name_space, если такого нет в скрипте сервера (а там пример с сервером). testfunc там тоже в корне.

62

Re: библиотека lua_share (обмен данными между скриптами lua)

смотрите примеры. они все работают.

"test_name_space" нет, потому что GetIPCNameSpace() создает такой неймспейс (таблицу), если ее нет, или использует существующую, если она уже есть: создана при загрузке lua_share_boot.lua, или позже. такой механизм нужен для того, чтобы кастомизировать поведение таблицы при помощи метаметодов, подробнее про это можно почитать в справке lua: https://www.lua.org/manual/5.3/manual.html#2.4

впрочем, для RPC вызова сам неймспейс не имеет значения, так как вызываемая функция должна находиться в корне. это сделано для простоты, иначе функции нужно было бы помещать в таблицы, что имеет смысл, но не очень удобно.

63

Re: библиотека lua_share (обмен данными между скриптами lua)

да, "вызовы будут выстраиваться в очередь с ожиданием". порядок обслуживания, вопрочем, не определен, проснуться может любой ожидающий тред, а не первый в очереди, так работает ожидание на мьютексе в windows.

64 (2021-06-11 02:30:35 отредактировано Андрей_)

Re: библиотека lua_share (обмен данными между скриптами lua)

toxa пишет:

смотрите примеры. они все работают.

"test_name_space" нет, потому что GetIPCNameSpace() создает такой неймспейс (таблицу), если ее нет, или использует существующую, если она уже есть: создана при загрузке lua_share_boot.lua, или позже. такой механизм нужен для того, чтобы кастомизировать поведение таблицы при помощи метаметодов, подробнее про это можно почитать в справке lua: https://www.lua.org/manual/5.3/manual.html#2.4

впрочем, для RPC вызова сам неймспейс не имеет значения, так как вызываемая функция должна находиться в корне. это сделано для простоты, иначе функции нужно было бы помещать в таблицы, что имеет смысл, но не очень удобно.

Итак, у меня не сервер, а ф-ция test в boot.

local sh = require "lua_share"
local ns = sh.GetNameSpace("test_name_space")
ns('test','a')

Выдает attempt to call a table value (local 'ns'). Вот и спрашиваю, как вызывать. Аналогия с IPC сервера не проходит. А мы здесь сегодня только про синхронизацию потоков скриптов внутри одного квика и без сервера.

Что-то мне подсказывает, что вы скажете добавлять lua_share_server.lua как скрипт в квик, запускать его, и именно его поток, а не потоки тех скриптов, будет выполнять ф-цию. Что не правильно, т.к. он один, а ф-ций много. Синхронизовать надо per-function, а не 1 поток на все функции\объекты. С классическим мьютексом потоки макросов бы сами исполняли код. С блокировкой per-mutex (объект)

65

Re: библиотека lua_share (обмен данными между скриптами lua)

Андрей_ пишет:

local sh = require "lua_share"
local ns = sh.GetNameSpace("test_name_space")
ns('test','a')

да, сейчас так нельзя, но, наверное, можно доделать. и, возможно, даже ns:test("a"), как у меня будет время, я посмотрю. пока же, можно воспользоваться метаметодами.

многопоточность в lua-машине (несколько тредов одновременно, а не по-очереди) возможна только в криво патченной lua-машине в квике. они сделали какого-то супер-кривого монстра соединив там луа-машины разных версий, естественно, это работает очень хреново. сейчас lua_share может работать с обычной vanilla lua, пример: телеграм-бот. вся эта многопоточная херня с мьютексами и блэк-джеком работать в ней не будет. по-этому я даже не рассматриваю тот вариант, который вы предлагаете.

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

66 (2021-06-11 13:01:11 отредактировано Андрей_)

Re: библиотека lua_share (обмен данными между скриптами lua)

toxa пишет:

пока же, можно воспользоваться метаметодами.

ок, тогда возвращаемся к примеру queue. Почему нельзя добавить в него такую ф-цию test и вызывать ее атомарно так же, как вызываются метаметоды? Какие метаметоды помимо index,newindex доступны для реализации в таблицах boot, чтобы они вызывались атомарно? Можно ли дать возможность произвольно добавлять им ф-ции и чтобы они были доступны в скрпитах?

А, ну собственно это развитие предыдущего вопроса. Это было бы queue:test("a"). Значит этого пока нет, но не ясно, что же есть помимо index,newindex. Для реализации здесь имитации мьютекса надо 2 метода, соответствующих get и free. Неужто только index,newindex можно задействовать? И имитация будет, как я понимаю, циклом sleep в get, пока уже взят.

67 (2021-06-11 13:22:48 отредактировано toxa)

Re: библиотека lua_share (обмен данными между скриптами lua)

Андрей_ пишет:

ок, тогда возвращаемся к примеру queue. Почему нельзя добавить в него такую ф-цию test и вызывать ее атомарно так же, как вызываются метаметоды?


да, это можно сделать, я же написал, как будет возможность. я помню, пытался это сделать, но что-то меня остановило. наверное, невозможность отличить в коде библиотеки ns:<method>() от ns["method"]

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

по крайней мере, вызов глобальных функций сделать точно можно.

Андрей_ пишет:

А, ну собственно это развитие предыдущего вопроса. Это было бы queue:test("a"). Значит этого пока нет, но не ясно, что же есть помимо index,newindex

я давал ссылку выше.

68 (2021-06-11 15:44:01 отредактировано Андрей_)

Re: библиотека lua_share (обмен данными между скриптами lua)

toxa пишет:

я давал ссылку выше.

Я знаю, какие бывают в lua. Спросил, какие из них поддерживаются библиотекой для атомарного исполнения после взятия их как GetNameSpace. Мы выяснили, что __call с передачей имени внутреннего метода и параметров - не работает. а __index работает.. Тогда какие метаметоды работают и почему только они?

69 (2021-06-11 21:02:35 отредактировано toxa)

Re: библиотека lua_share (обмен данными между скриптами lua)

Андрей_ пишет:

Я знаю, какие бывают в lua. Спросил, какие из них поддерживаются библиотекой для атомарного исполнения после взятия их как GetNameSpace.

все

Андрей_ пишет:

Просто не понятно, почему метаметоды работают, а просто методы нет.

попробую объяснить. у нас есть две разные lua-машины, не связанные между собой. первая (1) управляется квиком, вторая (2) - библиотекой lua_share.

когда в (1) исполняется код ns:test('a'), то в библиотеку приходит запрос "достань элемент "test" из таблицы ns". мы его достаем из (2) и создаем его копию в (1). это работает для данных, но не работает для кода, потому что тех объектов, которые есть в (2) нет в (1), и даже если байт-код скопировать, он не запустится. да нам и не нужно, чтобы он запускался в (1), нам нужно чтобы он запускался в (2), в этом весь смысл.

ну а дальше, библиотека lua_share ничего не знает о том, что будет происходить с элементом "test". обработчик внутри lua_share.dll завершился, все, забыли. дальше (1) действует самостоятельно. если код действительно был ns:test('a'), то (1) проверяет тип объекта и вызывает его, если тип - функция, если нет - выпадает в ошибку. нам нужно на каждый элемент типа "функция", который мы достаем из (2) создать proxy-объект "функция" внутри (1), который будет хранить информацию о том, что нужно вызывать внутри (2). но проблема еще в том, что время жизни объектов в (1) и (2) разное, нужно сделать так, чтобы функция не была уничтожена в (2) пока ее прокси существует в (1).

а метаметоды работают, поскольку они вызываются lua-машиной (2) когда мы обращаемся к объектам внутри (2). они не вызываются напрямую из (1).

возов ns("test", "a") работает потому, что библиотека получает указание вызвать объект ns, который ей принадлежит. тут все просто. когда мы можем отличить вызов от чтения/записи проблем нет. он не работает с ns который не IPC потому, что я просто не дописал небольшой кусок, видимо, хотел сделать как описано выше.

70 (2021-06-11 16:19:02 отредактировано toxa)

Re: библиотека lua_share (обмен данными между скриптами lua)

вот, объект типа NameSpace конструируется без обработчика __call:
https://github.com/untoxa/lua_share/blo … n.pas#L278

а объект типа IPCNameSpace с обработчиком __call:
https://github.com/untoxa/lua_share/blo … n.pas#L288

надо просто дописать имплементацию call для случая NameSpace. но ns("test", "a") не очень красиво, все же ns:test("a") гораздо круче.

71 (2021-06-12 01:41:17 отредактировано Андрей_)

Re: библиотека lua_share (обмен данными между скриптами lua)

toxa пишет:

когда в (1) исполняется код ns:test('a'), то в библиотеку приходит запрос "достань элемент "test" из таблицы ns". мы его достаем из (2) и создаем его копию в (1). это работает для данных, но не работает для кода, потому что тех объектов, которые есть в (2) нет в (1), и даже если байт-код скопировать, он не запустится. да нам и не нужно, чтобы он запускался в (1), нам нужно чтобы он запускался в (2), в этом весь смысл.

ну а дальше, библиотека lua_share ничего не знает о том, что будет происходить с элементом "test". обработчик внутри lua_share.dll завершился, все, забыли. дальше (1) действует самостоятельно. если код действительно был ns:test('a'), то (1) проверяет тип объекта и вызывает его, если тип - функция, если нет - выпадает в ошибку. нам нужно на каждый элемент типа "функция", который мы достаем из (2) создать proxy-объект "фнккция" внутри (1), который будет хранить информацию о том, что нужно вызывать внутри (2). но проблема еще в том, что время жизни объектов в (1) и (2) разное, нужно сделать так, чтобы функция не была уничтожена в (2) пока ее прокси существует в (1).


приходит запрос "достань элемент "test" из таблицы ns". А можно ли, чтобы сразу приходил запрос "запустти элемент "test" с параметром "a" и верни результат"? Без прокси и времен жизни, а сразу исполнили в (2) код, вернули данные.
С ns('test','a') это проще. И , в принципе, если дело только в синтаксисе, то можно и не усложнять реализацию.

72 (2021-06-12 01:43:35 отредактировано toxa)

Re: библиотека lua_share (обмен данными между скриптами lua)

Андрей_ пишет:

приходит запрос "достань элемент "test" из таблицы ns". А можно ли, чтобы сразу приходил запрос "запустти элемент "test" с параметром "a" и верни результат"? Без прокси и времен жизни, а сразу исполнили в (2) код, вернули данные.

нет, нельзя, потому что:

    local ns = sh.GetNameSpace("my_fancy_object")
    local method = ns["my_fancy_method"]
    -- some code here
    -- ...
    local result = method("a", "b", "c")

в смысле механизма исполнения это ничем не отличается от:

    local ns = sh.GetNameSpace("my_fancy_object")
    local result = ns.my_fancy_method("a", "b", "c")

и еще нужно не забывать, что возможно такое:

    local ns = sh.GetNameSpace("my_fancy_object")
    local result = ns.my_subtable.my_other_subtable.my_fancy_method("a", "b", "c")

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

73

Re: библиотека lua_share (обмен данными между скриптами lua)

И еще вопрос. На каком уровне сериализация потоков при вызове метаметодов? По объектно или глобально для всей общей памяти (в каждый момент только 1 поток исполняет код из boot)? Надо, чтобы объект общей памяти не блокировал доступ к другим. Т.е. по мьютексу на объект.

74 (2021-06-12 01:50:20 отредактировано toxa)

Re: библиотека lua_share (обмен данными между скриптами lua)

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

можно, как предлагалось много ранее тут, сделать на очередях.

75

Re: библиотека lua_share (обмен данными между скриптами lua)

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