Delphi: Асинхронный запуск процедур в потоке

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by Mixon, 9 Dec 2012.

  1. Mixon

    Mixon Elder - Старейшина

    Joined:
    12 Mar 2008
    Messages:
    394
    Likes Received:
    119
    Reputations:
    12
    Нуждаюсь в "асинхронном" запуске процедур (НЕ ПОТОКИ, скорее события который запускают код параллельно).
    В основном потоке (форме) это всё решается через мини-библиотеку OneShotTimer, в которой есть SetTimeout, и выходит что для асинхронного запуска надо всего-лишь => SetTimeout(proc,0); и он запускает процедуру параллельно

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

    Кто что посоветует?

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

    п.п.с. вот сама либа OneShotTimer, если кому интересно:
    Code:
    unit OneShotTimer;
    
    interface
    
    uses
      Windows, Generics.Collections;
    
    type
      TOnTimerProc = reference to procedure;
      procedure SetTimeout(AProc: TOnTimerProc; ATimeout: Cardinal);
    
    var
      TimerList: TDictionary<UINT_PTR, TOnTimerProc>;
    
    implementation
    
    procedure TimerProc(hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR;
      dwTime: DWORD); stdcall;
    var
      Proc: TOnTimerProc;
    begin
      if TimerList.TryGetValue(idEvent, Proc) then
      try
        KillTimer(0, idEvent);
        Proc();
      finally
        TimerList.Remove(idEvent);
      end;
    end;
    
    procedure SetTimeout(AProc: TOnTimerProc; ATimeout: Cardinal);
    begin
      TimerList.Add(SetTimer(0, 0, ATimeout, @TimerProc), AProc);
    end;
    
    initialization
      TimerList := TDictionary<UINT_PTR, TOnTimerProc>.Create;
    finalization
      TimerList.Free;
    
    end.
    // http://stackoverflow.com/a/10469878
     
  2. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    936
    Likes Received:
    162
    Reputations:
    27
    При событиях код запускается не параллельно, а последовательно, то есть синхронно. Если в OnClick кнопки написать sleep(5000); то вся форма зависнет на 5 сек.

    Посмотри на эти библиотеки, может быть что подойдет:
    AsyncCalls, OmniThreadLibrary
     
  3. Mixon

    Mixon Elder - Старейшина

    Joined:
    12 Mar 2008
    Messages:
    394
    Likes Received:
    119
    Reputations:
    12
    Они оба "играются" с потоками (отдельными) и синхронизациями, а меня это не устраивает, мне надо запустить выполнение кода в ЭТОМ-ЖЕ потоке на вторым действием, параллельным, БЕЗ СОЗДАНИЯ ОТДЕЛЬНОГО ПОТОКА, в этом и весь-то гемор!
     
  4. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    936
    Likes Received:
    162
    Reputations:
    27
    Вообще то там потоки создаются только один раз и добавляются в пул. Если хочешь, чтобы SetTimer работал в каком то другом потоке (не в главном), сделай в этом потоке Message Loop.
     
  5. Mixon

    Mixon Elder - Старейшина

    Joined:
    12 Mar 2008
    Messages:
    394
    Likes Received:
    119
    Reputations:
    12
    Последние 4 часа с этим и тружусь.
    Мессаж луп - весь поток повесит если в цикле, надо как-то из основного потока передавать мессаги в поток, я щас тружусь-тружусь но пока ничего
    Code:
        procedure WM_TIMER(var Msg: TMessage); message WM_TIMER; // эта процедура в основном потоке
    событие просто тупо не срабатывает:(
     
  6. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    936
    Likes Received:
    162
    Reputations:
    27
    Уточни еще кое что. Тебе нужно запустить код с помощью события в дополнительном потоке. Какой поток должен инициировать вызов? Главный или тот самый дополнительный, который позже вызовет твою функцию? Судя по всему второй вариант.

    Тогда твой алгоритм может выглядеть примерно так:
    0. Выполняешь в потоке любой код и в нем с помощью этой же библиотеки OneShotTimer делаешь вызовы функции SetTimeout с теми параметрами, которые тебе требуются и в нужном тебе количестве.
    1. Запускаешь Message Loop.

    Все, этого должно быть достаточно, чтобы твои функции, которые ты вызвал с помощью SetTimeout, выполнились.
    Зачем? Если из основного потока нужно делать асинхронные вызовы, лучше используй одну из двух библиотек, которые написал выше. Также можешь посмотреть код метода TThread.Synchronize, он запускает код из дополнительного потока в основном, но работает как раз с помощью посылки сообщения основному потоку.
     
  7. Mixon

    Mixon Elder - Старейшина

    Joined:
    12 Mar 2008
    Messages:
    394
    Likes Received:
    119
    Reputations:
    12
    Вариант 2.
    Нет, Ты не понял!
    Поток никак не должен быть связан с главным (в идеале)

    в самом потоке, работающем в реальном времени - нужно вызвать без задержки самого потока процедуру

    т.е.

    procedure execute;
    begin
    .............
    .............
    .............
    .....код.....
    .............
    .............
    .............
    ChechServerOnBalance; // запрос сам выполняется допустим 30 секунд к серваку, т.е. подвиснет на 30 сек интерфейс, а мне нужно чтоб он запустился ПАРАЛЕЛЬНО и не в потоке, так-же как и запускает таймер всё.
    .............
    ..работаем далее, без зависаний, пока проверяется баланс..
    .............
    end;

    пример с "проверкой баланса" дан как пример понять смысл, аналогично ему будет sleep(30000);, т.е. по сути - там не запрос, а другая процедура с другими назначениями.

    еще как пример, дабы больше сделать понимание проблемы (уже ближе к сути)

    раньше был асинхронный запрос по инету, т.е.
    d:=Create(True);
    d.Send(...)
    d.Resume;

    и далее работает код...........
    а потом, когда приходит ответ - этот запрос вызывает callback в потоке, и он выполяется

    вот мне надо сделать аналог этой-же ситуации, но без создания потока, т.е. запрос отправился - он интерфейс ОТПУСТИЛ, код дальше выполнился. а ответ пришёл - он дёрнул каллбеком его
    ибо сейчас получается что идёт: запрос => выполняется, и вызывает каллбек1, каллбек1 шлёт запрос2 => вызывается каллбек2, который шлёт запрос 3 =>............
    и получается, что связь полностью синхронная, и засчёт этого если есть код, ПОСЛЕ посылки запроса - то он выполнен не будет пока вся цепочка запросов-каллбеков не дойдёт до конца

    а мне надо его послать => выполнить код под ним => ожидать пока он дёрнет каллбек

    п.с. и даже как я понимаю - то он даже не параллельно выполняется (вот только допёр) а просто останавливает выполнение основного потока и выполняет себя потом, но поскольку даже 100ms хватает чтоб выполнить все "нижние" процессы то кажется будто он выполняется параллельно основному коду. Но всё-же это лучше, когда он "отпускает интерфейс" и даёт выполнятся коду далее, этого и надо достичь.
     
    #7 Mixon, 11 Dec 2012
    Last edited: 11 Dec 2012
  8. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    936
    Likes Received:
    162
    Reputations:
    27
    Mixon, все ясно. То что ты описал - это асинхронный ввод/вывод. В Delphi для этого есть хорошая библиотека Overbyte ICS. Она как раз позволяет выполнить асинхронный запрос, например, на скачивание какой нибудь страницы по протоколу HTTP и обработать ответ с помощью коллбека после того, как он придет. При этом после отправки запроса программа продолжит свое выполнение, а не как в случае, например, с теми же Indy или Synapse зависнет. Message Loop нужен обязательно.
     
  9. Mixon

    Mixon Elder - Старейшина

    Joined:
    12 Mar 2008
    Messages:
    394
    Likes Received:
    119
    Reputations:
    12
    Ну тк а как мне этот мессаже-луп так всунуть чтоб он-то поток не подвешивал???
    п.с. зайди в аську плиз, так проще будет!)))
     
    #9 Mixon, 11 Dec 2012
    Last edited: 11 Dec 2012
  10. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    936
    Likes Received:
    162
    Reputations:
    27
    0. Выполняешь любой код, запускаешь асинхронные запросы. В коллбеках на эти запросы должен быть, нужный тебе код а также запуск новых запросов, если надо (то есть цепочка запросов получается).
    1. Message Loop.

    В любом случае цикл сообщений "подвешит" твой поток, но благодаря колбекам на запросы он будет "оживать", обрабатывать запросы и запускать новые.
     
    #10 Chrome~, 11 Dec 2012
    Last edited: 11 Dec 2012
  11. Mixon

    Mixon Elder - Старейшина

    Joined:
    12 Mar 2008
    Messages:
    394
    Likes Received:
    119
    Reputations:
    12
    как я понял - ICS - это тупо другая либа аналог сунапса но без позвешивания.
    а мне нужно конкретно с сунапсом сделать так
     
  12. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    936
    Likes Received:
    162
    Reputations:
    27
    С Synapse получится сделать только через пул потоков и никак иначе, ибо Synapse сделан для синхронной работы с сетью, тогда как ICS - для асинхронной.
     
  13. mironich

    mironich Elder - Старейшина

    Joined:
    27 Feb 2011
    Messages:
    733
    Likes Received:
    73
    Reputations:
    19
    Синапс юзает блокирующие сокеты...
    Асинхронного с ним нечего не сваришь.