Что нового

Предварительный запуска файла

winkot

Новичок
Сообщения
189
Репутация
0
В связи с тем что AutoIt нет многопоточности, думаю в качестве потоков использовать другие exe Файлы. Имеет ли смысл предварительно загружать их в память для увеличение быстродействие, а не ждать момента запуска? И как это можно сделать?
 

Prog

Продвинутый
Сообщения
590
Репутация
72
Нет там многопоточности что не удивительно. Потоки запускаются в контексте процесса и они делят между собой его виртуальное адресное пространство. То есть переменные общие и если запустить этот код
Код:
#NoTrayIcon
#include 'N.au3'

global $Counter = 0

func task()
  MsgBox(0, '', $Counter)
endfunc

func main()
  $Counter += 1
  local $t = NRun('task')
  NWait($t)
endfunc

NMain('main')
в окошке должно быть число 1, но будет 0.
 

Prog

Продвинутый
Сообщения
590
Репутация
72
оно не работает?
Оно непонятно как работает.
Автор написал

How it works?​

Please see the C++ source code, I'll update soon
То есть он предлагает изучать исходник dll чтобы это понять.

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

Это не многопоточноть, а запуск копии скрипта в другом потоке.
 

damien2008

Осваивающий
Сообщения
185
Репутация
34
Нет там многопоточности что не удивительно. Потоки запускаются в контексте процесса и они делят между собой его виртуальное адресное пространство. То есть переменные общие и если запустить этот код
Код:
#NoTrayIcon
#include 'N.au3'

global $Counter = 0

func task()
  MsgBox(0, '', $Counter)
endfunc

func main()
  $Counter += 1
  local $t = NRun('task')
  NWait($t)
endfunc

NMain('main')
в окошке должно быть число 1, но будет 0.
это сюр.
вы вообще что-нибудь читали, о том как работают потоки?
и что такое области видимости?
нельзя просто так взять, и передать одну переменную с
одного потока в другой!
+ если будет одновременное обращение к одной и той-же
переменной с разных потоков, приложение может упасть,
это к вопросу о синхронизаци..

вот из примера 1:
глобальная переменная
Код:
global $g = NGlobal()


вот из примера 2:
локальная переменная
Код:
local $state = NLocal()


дальше, из примера 2 (test_2.au3):
Код:
; Run task with state.
NRun('task', $state)


по нажатию кнопки
меняется состояние:
Код:
$state.ready = true


а в таске
Код:
; task
func task($state)


в бесконечном цикле проверяется этот же стейт:
Код:
while true
; Change GUI title when ready.
if $state.ready then


я в шоке с вас вы примеры смотрели??. вот сделал пример с гуи:

Код:
#NoTrayIcon ; Should add this to prevent tray icon in sub-thread.
#include 'N.au3'

; This test create a new thread worker that changes GUI title randomly.

; task1
func task1($state)
    local $gui = HWnd($state.hwnd)
    while true
        if $state.ready then
            local $n = Random(0xFF, 0xFFFFFF, 1)
            WinSetTitle($gui, '', 'Random: ' & $n)
            ControlSetText($gui, "", $state.label1, $state.first)
            $state.first += 1
        endif
        Sleep(20)
    wend
endfunc

; task2
func task2($state)
    local $gui = HWnd($state.hwnd)    ; .hwnd is casted to int, just convert it to HWND
    while true
        if $state.ready then
            ControlSetText($gui, "", $state.label2, $state.second)
            $state.second += 1
        endif
        Sleep(20)
    wend
endfunc

; main
func main()
    ; Create GUI with 3 buttons.
    local $gui = GUICreate('Test 3', 320, 150)
    local $l1 = GUICtrlCreateLabel("label1", 10, 30)
    local $l2 = GUICtrlCreateLabel("label2", 70, 30)

    local $btn = GUICtrlCreateButton('Start', 20, 60, 80, 30)
    GUISetState(@SW_SHOW)

    ; Create shared state.
    local $state = NLocal()
    $state.hwnd = $gui    ; GUI handle
    $state.ready = false    ; ready state
    $state.first = 1
    $state.second = 1
    $state.label1 = $l1
    $state.label2 = $l2

    ; Run task with state.
    NRun('task1', $state)
    NRun('task2', $state)

    ; GUI message loop.
    while true
        switch GUIGetMsg()
            ; close
            case -3
                exit

            ; button 1
            case $btn
                if $state.ready then
                    $state.ready = false
                    GUICtrlSetData($btn, 'Start')
                else
                    $state.ready = true
                    GUICtrlSetData($btn, 'Stop')
                endif
        endswitch
    wend
endfunc

; Execute main entry.
NMain('main')


3 независимых цикла While !! как это нету многопоточности?!
100 раз уже обсуждалась тема многопоточности в автоит.
понятно, что это хак, но а какая вам еще нужна многопоточность?
 
Последнее редактирование:

Prog

Продвинутый
Сообщения
590
Репутация
72
вы вообще что-нибудь читали, о том как работают потоки?
и что такое области видимости?
нельзя просто так взять, и передать одну переменную с
одного потока в другой!
Не путайте процессы и потоки. У потоков в пределах процесса общее виртуальное адресное пространство.
Функция CreateThread
Создает поток для выполнения в виртуальном адресном пространстве вызывающего процесса.
Все переменные общие для потоков в процессе.
Поэтому вы не правы. Переменные изначально доступны во всех потоках процесса без всякий ухищрений.

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

вот из примера 1:
глобальная переменная
Пример это замечательно, но где документация? Она нужна чтобы понять как использовать те или иные средства.
Представьте что у AutoIt нет, а только несколько примеров. Было бы очевидно как работает та или иная функция?

3 независимых цикла While !! как это нету многопоточности?!
Это не "True Multi-threading", а немного другое.
Да потоки в dll создаются, но в них выполняется новый экземпляр exe файла запускаемый из памяти. Посмотрите исходники и убедитесь в этом.
Это не настоящая многопоточность. Это почти тоже самое что запустить несколько процессов и назвать это многопотностью.
 

damien2008

Осваивающий
Сообщения
185
Репутация
34
Все переменные общие для потоков в процессе.
Поэтому вы не правы. Переменные изначально доступны во всех потоках процесса без всякий ухищрений.
это не "thread-safe" практика.
они-то доступны, но приложение упадет, когда вы попытаетесь одновременно из двух потоков изменить 1 переменную.

я не спорю о процессах и потоках, понятно что это разные вещи.

"новый экземпляр exe файла запускаемый из памяти"
там не новый ехе запускается каждый раз из памяти, там ехе читается 1 раз в память,
а потом везде используется AutoItObject.

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

мы ж тут про автоит говорим или где?

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

be cool

та и вообще я не понимаю, о чем мы спорим.
вы хотите, чтобы автоит был как с#, с, или делфи?
можете прочитать у майкрософта за потоки и задачи, там сказано, что потоки даже если их и много,
все-равно будут выполнятся синхронно, это просто процессор будет переключатся между потоками,
тем самым, ваше приложение будет выполнятся медленно. именно для этого в дотнет фреймворке,
этот менеджер уже создали, протестировали и рекоммендуют использовать TaskFactory,
конечно вы сами тоже можете использовать и таски и треды самостятельно, но когда вы будете
делать более-менее серьезное приложение, к-е работает с сетью, базой данных или диском,
вы все-равно придете к тому, что будете создавать свой менеджер этих задач, или потоков,
и они рекоммендуют использовать максимум 2-5 штук,
как и все приходят, ну, то-есть, да вы тоже создадите под себя свой велосипед, и ок.
вы можете даже сейчас с помощью этой библиотеки создать свой менеджер этих потоков, определить что будет мьютекс,
определить разрешения на чтение, запись, синхронизировать все это между собой, эта библиотека более чем позволяет это сделать..
 
Последнее редактирование:

Prog

Продвинутый
Сообщения
590
Репутация
72
это не "thread-safe" практика.
Я выше писал, при необходимости доступа потоков к общим ресурсам используют мьютекс.

"новый экземпляр exe файла запускаемый из памяти"
там не новый ехе запускается каждый раз из памяти, там ехе читается 1 раз в память,
а потом везде используется AutoItObject.
Несколько функций из dll.
C++:
static void CALLBACK N_ThreadRoutine(Thread *thread)
{
    // Execute main entry point.
    WinMain_t main = (WinMain_t)N_MemoryModuleGetEntryPoint(thread->module);
    main(GetModuleHandleA(NULL), NULL, GetCommandLineW(), 0);
}

DWORD WINAPI NAPI_Run(LPCSTR fn, LPDISPATCH local)
{
    DWORD id;
    Thread *thread = new Thread();

    // Load current EXE with MemoryModule.
    SIZE_T moduleSize;
    LPVOID moduleData = N_GetBinaryModule(&moduleSize);
    thread->module = MemoryLoadLibrary(moduleData, moduleSize);

    // Save thread's function name.
    lstrcpyA(thread->func, fn);
    // Save local shared.
    thread->local = local;
    // Create suspended thread.
    thread->handle = CreateThread(NULL, 0,
        (LPTHREAD_START_ROUTINE)&N_ThreadRoutine, (LPVOID)thread, CREATE_SUSPENDED, &id);

    // Add local object ref.
    if (local != NULL)
        local->AddRef();

    // Insert to map and resume it.
    g_threadMap[id] = thread;
    ResumeThread(thread->handle);

    return id;
}
NAPI_Run() выполняется при вызове NRun() из AutoIt.
В потоке запускается функция N_ThreadRoutine() в которой вызывается стартовая функция exe файла. Таким образом запускается новый экземпляр приложения в потоке.

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

какая документация?
Такая в которой написано как правильно пользоваться этой библиотекой.

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

damien2008

Осваивающий
Сообщения
185
Репутация
34
:good: восьмиядерные.
переключение контекста+
 
Последнее редактирование:

Prog

Продвинутый
Сообщения
590
Репутация
72
Код:
#NoTrayIcon
#include 'N.au3'

global const $Count=2 ; Число потоков
global $g = NGlobal()

func task($p)
    ConsoleWrite($p.num&@CRLF)
    while $p.break = false
        $g.aTask($p.num) += 1
    wend
endfunc

func main()
    dim $t[$Count]
    dim $p[$Count]

    for $i=0 to $Count-1
        $g.aTask($i)=0
        $p[$i] =  NLocal()
        $p[$i].break = false
        $p[$i].num = $i
        $t[$i] = NRun('task', $p[$i])
    next

    Sleep(1000)
    for $i=0 to $Count-1
        $p[$i].break = true
    next

    NWaitAll()
    ConsoleWrite(@crlf)

    Local $All = 0
    for $i=0 to $Count-1
        ConsoleWrite($g.aTask($i)&@crlf)
        $All += $g.aTask($i)
    next

  ConsoleWrite("All="&$All&@CRLF)
endfunc

NMain('main')
1 поток. Производительность больше 300 тысяч итераций цикла.
2 потока и всего 17 тысяч итераций.
Синхронизация уменьшила производительность в 20 раз?

Без потоков 2 миллиона итераций.
Код:
#NoTrayIcon

global $g[1], $Br=false

func task()
    while $Br = false
        $g[0] += 1
    wend
endfunc

Func BreakLoop()
    $Br=True
EndFunc

AdlibRegister("BreakLoop", 1000)

task()

ConsoleWrite($g[0]&@CRLF)
 

damien2008

Осваивающий
Сообщения
185
Репутация
34
вот примерно:
1.au3 = 65.1079854907249, максимум 75 мс
Код:
#NoTrayIcon
#include 'N.au3'

global $g = NGlobal()

func task($g)
    Local $hTimer = TimerInit()

    local $b = 0
    For $i = 0 To 100000 Step 1
        $b += 1
    Next

    $g.res = TimerDiff($hTimer)
endfunc

func main()
    $g.res = 0
    NRun('task', $g)
 
    While true
        If $g.res > 0 Then
        ConsoleWrite($g.res & @CRLF)
        ExitLoop
        endif
    wend
endfunc

NMain('main')

2.au3 = 64.6865862090028
Код:
#NoTrayIcon

func task()
    Local $hTimer = TimerInit()
 
    local $b = 0
    For $i = 0 To 100000 Step 1
        $b += 1
    Next

    $g = TimerDiff($hTimer)
    ConsoleWrite($g & @CRLF)
endfunc

task()
 
Последнее редактирование:

Prog

Продвинутый
Сообщения
590
Репутация
72
За 10 миллисекунд выполняет.

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

damien2008

Осваивающий
Сообщения
185
Репутация
34
69.0896231087815
69.4663654780804
Код:
#NoTrayIcon
#include 'N.au3'

global $g = NGlobal()

func task($g)
    Local $hTimer = TimerInit()

    local $b = 0
    For $i = 0 To 100000 Step 1
        $b += 1
    Next

    $g.res = TimerDiff($hTimer)
endfunc

func task2($g)
    Local $hTimer = TimerInit()

    local $b2 = 0
    For $i2 = 0 To 100000 Step 1
        $b2 += 1
    Next

    $g.res2 = TimerDiff($hTimer)
endfunc

func main()
    $g.res = 0
    $g.res2 = 0
 
    NRun('task', $g)
    NRun('task2', $g)
 
    While true
        If $g.res > 0 And $g.res2 > 0 Then
        ConsoleWrite($g.res & @CRLF)
        ConsoleWrite($g.res2 & @CRLF)
        ExitLoop
        endif
    wend
endfunc

NMain('main')

по факту ConsoleWrite много сьедает 1-2мс, так что тоже не обьективно.
 
Верх