Конференция "Начинающим" » Помогите написать работу с потоком [D7]
 
  • Kirill © (22.08.17 11:31) [0]
    Добрый день.
    Есть программа работы с кассовым аппаратом. В отдельном модуле реализовано хранение чеков в БД, в отдельном работа с web-сервисом (обмен json-данных) с 1С.

    Необходимо написать отправку данных в 1С в отдельном потоке. Программа должна делать выборку из БД данных, которые не отправлены и отправлять в 1С. В случае успешной отправки помечать запись как отправленную. К сожалению, у меня совсем не получается постичь работу в Delphi с потоками, поэтому пишу в эту конференцию.

    Вопросы по теме:
    1. Как лучше делать выборку неотправленных  данных? По одной записи или все неотправленные и все отправлять? Чтобы фризов в основной программе не было.
    2. Как правильно описать Объект потока? В основном потоке уже есть открытое соединение с БД. Надо будет создавать новое соединение?
    3. Как правильно вызвать этот поток? При запуске программы (открытии основной формы) или по таймеру запускать (например каждую минуту)?

    Delphi 7. БД: Firebird 2.5.7 embed. WS: Synapse и lkJSON.
  • KilkennyCat © (22.08.17 12:20) [1]

    > Чтобы фризов в основной программе не было.

    вроде как, эту проблем уже решает отдельный поток
    1. да как угодно. если объем данных небольшой, соединение устойчивое - все сразу.
    2. если отправлять по одной записи, то к бд вообще нет смысла обращаться, данные уже есть (внесение в бд же ими делается)
    3. если отправлять по одной записи, то сразу при появлении данных. если все сразу - то по завершении кассовой смены.
  • KilkennyCat © (22.08.17 12:21) [2]
    а еще это может регламентироваться законодательством, сразу или не сразу отправлять.
  • Kirill © (22.08.17 13:26) [3]
    Не, данные по законодательству отправляются самим аппаратом. А вот в 1С, по-хорошему, надо сразу видеть что оплатили, а что еще нет. А так как есть точки удаленные с нестабильным интернетом, надо предусмотреть что когда чек был пробит инета не было, а потом пытаться, пытаться, пытаться отправить.
  • Kirill © (22.08.17 15:27) [4]
    Можно простенький примерчик, в какой момент получать данные, в какой отправлять данные, получать ответ от веб-сервиса, записывать флаг об успешной отправке и, главное, в какой момент вызывать синхронизации с основным потоком, чтобы зависаний не было.
  • ухты © (22.08.17 16:21) [5]
    Synapse разве не умеет асинхронно работать?
  • Kirill © (22.08.17 16:34) [6]
    Нет. ICS асинхронно работает.
    Помимо этого хочу разобраться на небольшом но осязаемом примере как правильно использовать потоки.
  • kilkennycat © (22.08.17 17:56) [7]

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

    Это не простенький примерчик. И все эти моменты определяет программист. Про зависания вообще непонятно.
    Имеет смысл сначала всё-таки прочитать книжки по делфи, коих множество, и в коих есть простенькие примерчики отдельных задач. И  тогда вопрос либо отпадет, либо трансформируется в что-то более конкретное.
    А вот если бы я писал эту программу, то делал бы ее по ТЗ заказчика.
  • Kirill © (22.08.17 18:00) [8]

    procedure SendLocalDataToServer;
    var
     sthr : TSendThread;
    begin
     //
     sthr := TSendThread.Create(True);
     sthr.fhttp := http;
     sthr.Priority := tpLower;
     sthr.FreeOnTerminate := True;
    end;

    { TSendThread }

    procedure TSendThread.Execute;
    var s : string;
    begin
     inherited;
     while True do begin
       if Terminated then Break;
       if not DM.SelectDataForSend then begin
         Terminate;
         Break;
       end;
       DM.ibqryThread.First;
       if fhttp.PostPayData(
         DM.ibqryThread.FieldByName('chid').AsInteger,
         DM.ibqryThread.FieldByName('cheque_type').AsInteger,
         DM.ibqryThread.FieldByName('cheque_time').AsDateTime,
         DM.ibqryThread.FieldByName('aw_date').AsDateTime,
         DM.ibqryThread.FieldByName('client').AsString,
         DM.ibqryThread.FieldByName('aw_code').AsString,
         DM.ibqryThread.FieldByName('department').AsInteger,
         DM.ibqryThread.FieldByName('vat_type').AsInteger,
         DM.ibqryThread.FieldByName('quantity').AsFloat,
         DM.ibqryThread.FieldByName('price').AsFloat,
         DM.ibqryThread.FieldByName('summ').AsFloat,
         DM.ibqryThread.FieldByName('vatsumm').AsFloat,
         DM.ibqryThread.FieldByName('summ1').AsFloat,
         DM.ibqryThread.FieldByName('summ2').AsFloat,
         DM.ibqryThread.FieldByName('summ3').AsFloat,
         DM.ibqryThread.FieldByName('summ4').AsFloat,
         DM.ibqryThread.FieldByName('vat18').AsFloat,
         DM.ibqryThread.FieldByName('vat10').AsFloat,
         DM.ibqryThread.FieldByName('vat0').AsFloat,
         s) then begin
         // TODO
         // write code for mark this cheque as posted on server
       end;
     end;
    end;


    Может подскажете на таком примере правильное направление мысли?
  • KilkennyCat © (22.08.17 21:11) [9]
    здесь нет мысли ))) лишь небезопасная отправка кучки полей через http-post, и без примера обработки ошибок.
  • Kirill © (23.08.17 08:58) [10]
    Т.е. простенького примера (можно из комментариев) какой структуры вы бы написали объект потока, в каких процедурах и в какой момент делать выборку данных, отправку в 1с, запись в бд и вызов встроенных процедур самого потока в конференции новичков не дождешься? Н-да... страшно представить чтобы в остальных конференциях сказали и куда послали...
    Спасибо, что не отказали.
  • rrrrrrr © (23.08.17 09:20) [11]
    по коду:
    непонятно используется ли отдельная сессия для запроса из потока.

    по дизайну:
    эй, поток, я вон там оставил для тебя какие-то штуки.
    у тебя в юзез мой дм, плюс ты умный, так что сходи туда возьми все что надо (ты знаешь) и сделай что мне надо.
  • Kirill © (24.08.17 00:57) [12]
    Зря вы так... Спасибо брату, что нашел время и помог мне. Я, конечно, до сих пор не понял как с ним работать, но с его помощью и методом тыка написал такой код, который работает.

    constructor TSendThread.Create;
    begin
     fhttp := THttpService.Create;
     fTimeout := 1000;
     inherited Create(False);
    end;

    procedure TSendThread.SendData;
    var s : string;
    begin
     if not DM.SelectDataForSend then begin
       fTimeout := 5000;
     end
     else begin
       fTimeout:= 1000;
       with DM.ibqryThread do begin
         First;

         if fhttp.PostPayData(
           FieldByName('chid').AsInteger,
           FieldByName('cheque_type').AsInteger,
           FieldByName('cheque_time').AsDateTime,
           FieldByName('awbdate').AsDateTime,
           FieldByName('client').AsString,
           FieldByName('awbcode').AsString,
           FieldByName('department').AsInteger,
           FieldByName('vat_type').AsInteger,
           FieldByName('quantity').AsFloat,
           FieldByName('price').AsFloat,
           FieldByName('summ').AsFloat,
           FieldByName('vatsumm').AsFloat,
           FieldByName('summ1').AsFloat,
           FieldByName('summ2').AsFloat,
           FieldByName('summ3').AsFloat,
           FieldByName('summ4').AsFloat,
           FieldByName('vat18').AsFloat,
           FieldByName('vat10').AsFloat,
           FieldByName('vat0').AsFloat,
           s) then begin
           DM.SetChequePosted(FieldByName('chid').AsInteger);
         end;
       end;
     end;
    end;

    procedure TSendThread.Execute;
    begin
     while not Terminated do begin
       Synchronize(SendData);
       Sleep(fTimeout);
     end;
    end;


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

    Я от сообщества ожидал, что-то типа такого:

    procedure TThread.SendData;
    begin
    // выборка данных для отправки.
    // отправка данных
    // если отправка успешна, то фиксируем в бд
    end;

    procedure TThread.Execute;
    begin
     while not Terminated do begin
       Syncronize(SendData);
       Sleep(1000);
     end;
    end;



    Что сложного написать тому, кто это знает и главное понимает, эти 10 строк. Я тоже это знаю, и программировать на каких-то языках могу очень сложные приложения, а на каких-то только понять, что писал автор, но вот никак не могу усвоить в какой последовательности надо программировать работу с потоками в Delphi. И пример про сортировку разобрал из поставки, но как применить его конкретно на моем примере так и не понял.

    Спасибо. Вопрос закрыт
  • Германн © (24.08.17 01:45) [13]

    > Kirill ©   (22.08.17 16:34) [6]
    >
    > Нет. ICS асинхронно работает.

    Хм. ICS действительно работает как правило асинхронно. В это одна из главных её особенностей (но и синхронно тоже умеет). Но при чем тут ICS?
  • KilkennyCat © (24.08.17 07:25) [14]

    > Kirill ©
    > Что сложного написать тому, кто это знает и главное понимает,
    >  эти 10 строк.

    они есть практически в любой книжке. что сложного в чтении книжки? Например, у Тейксейры и Пачеко https://www.for-stydents.ru/informatika/delphi/uchebniki/delphi-5-rukovodstvo-razrabotchika-tom-1-osnovnye-metody-i-tehnologii-programmirovaniya.html есть даже "Многопоточный доступ к базе данных". Читали?
    И прочитайте https://www.opennet.ru/docs/RUS/smart_question/
  • Юрий Зотов © (24.08.17 07:25) [15]
    > Kirill ©   (24.08.17 00:57) [12]

    > Вопрос закрыт


    Вряд ли вопрос закрыт. Дело в том, что в такой редакции второй поток не имеет никакого смысла. Смотрим метод Execute:

    procedure TSendThread.Execute;
    begin
     while not Terminated do begin
       Synchronize(SendData);
       Sleep(fTimeout);
     end;
    end;


    Видим, что:

    1. Метод SendData вызывается через метод Synchronize - то есть, метод SendData все равно будет выполнен в главном (а не во втором) потоке.

    2. Sleep - в данном случае не дает ничего, кроме тормозов (ну разве что разгружает процессор, больше никакого прока от него здесь нет).

    3. Больше ничего второй поток не делает. Какой же тогда в нем смысл? Без него все будет работать точно так же.
  • rrrrrrr © (24.08.17 08:40) [16]
    плюс снова непонятно есть ли у брата отдельная от основной сессия с бд.
  • Kirill © (24.08.17 08:49) [17]

    > они есть практически в любой книжке. что сложного в чтении
    > книжки?

    А это из разряда смотрим в книгу, видим фигу. Это проблема моего мышления. Пока я не могу представить в голове, я этого не понимаю и не запоминаю. Я так с фуаном мучался в универе. Я ж говорю, и примеров полно и код рабочий был по потокам, ну не могу понять как он работает. Ответы Юрия тому пример.


    > Метод SendData вызывается через метод Synchronize - то есть,
    >  метод SendData все равно будет выполнен в главном (а не
    > во втором) потоке.

    Т.е. вызывать надо без него?


    > Sleep - в данном случае не дает ничего, кроме тормозов (ну
    > разве что разгружает процессор, больше никакого прока от
    > него здесь нет)

    Sleep не делает DDOS атаку на web-сервис 1С. Т.е. он отправляет неотправленные чеки с периодичностью каждую секунду, а если в последний раз не было ничего на отправку, то ждет уже 5 сек.

    > Больше ничего второй поток не делает. Какой же тогда в нем
    > смысл? Без него все будет работать точно так же.

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


    > плюс снова непонятно есть ли у брата отдельная от основной
    > сессия с бд.

    На счет его я не знаю, какая у него структура БД, а у меня нет. Надо новую? А если FB Embeded?
  • rrrrrrr © (24.08.17 08:52) [18]
    Sleep не делает DDOS

    вотэтоповорот. кто бы мог подумать.

    На счет его я не знаю, какая у него структура БД, а у меня нет. Надо новую? А если FB Embeded?

    уже пофик.
  • rrrrrrr © (24.08.17 08:55) [19]
    Смысл в том, чтобы пользователь продолжал работать с кассой, а сама программа отправляла подтверждения оплат не блокируя интерфейс.

    без этого потока все будет точно так же. тютя в тютю.
    даже немного более быстрее и менее тормознуто.
  • Inovet © (24.08.17 09:09) [20]
    > [17] Kirill ©   (24.08.17 08:49)
    > Смысл в том, чтобы пользователь продолжал работать с кассой,
    > а сама программа отправляла подтверждения оплат не блокируя
    > интерфейс.

    Способы реализации на Делфи здесь вторичны. Есть специальные объекты для синхроницации потоков. Поток кассы сигнализирует через такой объект, что у него появились новые чеки, поток отправки находится в спячке через вызов WaitForSingleObject, просыавется, отправляет эти данные и снова уходит в засыпание через вызов WaitForSingleObject (WaitForMultipleObject). Дополнительно надо обеспечить блокировку одновременного изменения данных двумя потоками - это уже от логики и структуры данных. Тепрь то же самое через обёртки этого хозяйства в Делфи.
  • Inovet © (24.08.17 09:12) [21]
  • Kirill © (24.08.17 09:12) [22]
    Это если на отправку только один последний чек, а если чеков наберется больше и скорость с сервисом будет не ахти, то фриз будет.
    Спасибо Synchronize убрал. Понял, что его надо использовать для работы с vcl данными, например обновить количество неотправленных данных, а так как у меня это не выводится, то и не нужно.
  • rrrrrrr © (24.08.17 09:15) [23]
    не ахти, то фриз будет.

    он и в твоем варианте с потоком "от брата" будет.
    точно такой же.
    как и без потока "от брата"
  • Inovet © (24.08.17 09:18) [24]
    > [22] Kirill ©   (24.08.17 09:12)
    > Это если на отправку только один последний чек, а если чеков
    > наберется больше и скорость с сервисом будет не ахти, то
    > фриз будет.

    Пока все не отправит в ожидание не уходить.
  • Kirill © (24.08.17 09:38) [25]
    Уважаемый, rrrrrrr.
    Вместо того, чтобы писать какой %%%%%% код вы написали с братом, лучше подсказали на этом примере как его исправить, чтобы он работал правильно и описали почему надо делать так.
    А с Вашим подходом можно любую программу критиковать: от блокнота до всей ОС.


    > Пока все не отправит в ожидание не уходить

    Извините, не понял. В смысле в этом потоке все данные сразу отправлять без пауз?
  • Inovet © (24.08.17 09:59) [26]
    > [25] Kirill ©   (24.08.17 09:38)
    > В смысле в этом потоке все данные сразу отправлять без
    > пауз?

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

    Возможна и другая логика. С таймаутом тоже - в каком его потоке опрабатывать, в кассовом или в отправляющем, стоит подумать, имхо в кассовом логичнее. Но суть синхронизации такая.
  • Inovet © (24.08.17 10:00) [27]
    > [25] Kirill ©   (24.08.17 09:38)
    > лучше подсказали на этом примере как его исправить, чтобы
    > он работал правильно и описали почему надо делать так.

    Так это в кижках надо читать, а не переписывать приведённый код.
  • rrrrrrr © (24.08.17 10:13) [28]
    как помочь ламеру,
    если у него все усилия уходят на упереться рогом и всеми копытами,
    доказывая, что написано правильно, в то время как написана бессмыслица.

    образец:

    > Больше ничего второй поток не делает. Какой же тогда в нем
    > смысл? Без него все будет работать точно так же.

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



    ответ : никак.
  • Kirill © (24.08.17 10:19) [29]

    > Кассовый поток накопил данные чеков

    Вот тут я бы точно не стал делать... С новым, блин, законом 54-ФЗ главное чтобы кассовый поток (он же основной) печатал чек и по результатам печати заносил в локальную бд информацию.
    Я кажется понял Вашу идею, но не уверен насчет целесообразности такого метода. Зачем "грузить" кассовый поток ненужной выборкой. Пусть работает с кассиром и оборудованием.


    > Так это в кижках надо читать, а не переписывать приведённый
    > код.

    Еще раз повторюсь, я в эту конференцию (Новичкам) не потому написал, что не знаю Delphi, а потому что не могу понять как на практике применить работу с потоками. Я и книги читал и примеры смотрел, ну не получается понять...
    На текущий момент убрал из последнего кода синхронизацию SendData. И с ним и без него не было фризов.

    Еще раз всем спасибо за обсуждение и критику.
  • Inovet © (24.08.17 10:23) [30]
    > [29] Kirill ©   (24.08.17 10:19)
    > Я кажется понял Вашу идею, но не уверен насчет целесообразности
    > такого метода. Зачем "грузить" кассовый поток ненужной выборкой.
    > Пусть работает с кассиром и оборудованием.

    Неправильно понял. Кассовый и так это всё делает, но должен просигналить отправляющему о готовности данных, иначе как тот узнает о их наличии.
  • rrrrrrr © (24.08.17 10:26) [31]
    а потому что не могу понять как на практике применить работу с потоками.

    ты не можешь понять гораздо более главного.

    начинать проектировать многопоточность (и не только) надо в терминах реального мира и своей предметной области.

    а ты же сразу начинаешь искать в каком порядке скомбинировать знакомые слова из делфи в надежде что какая-то комбинация решит задачу.
  • Kirill © (24.08.17 10:31) [32]

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

    В Execute потока бесконечный цикл с паузой и выборкой неотправленных данных (Поле POSTED = 0).
  • Inovet © (24.08.17 10:39) [33]
    > [32] Kirill ©   (24.08.17 10:31)
    > В Execute потока бесконечный цикл с паузой и выборкой неотправленных
    > данных (Поле POSTED = 0).

    ЮЗ выше в [15] Юрий Зотов ©   (24.08.17 07:25) уже сказал, что там. Перечитай.
  • sniknik © (24.08.17 10:53) [34]
    > Понял, что его надо использовать для работы с vcl данными
    прикол в том, что датамодуль, хттп, датасет на датамодуле, это все тоже vcl, т.е. по "букве" все правильно брат сделал, но по сути абсолютно неверно.

    вообще, все это (датамодуль, и все на нем лежащее) можно использовать в потоке, но только если абсолютно уверен, что к этому же нет обращений из других потоков, основного в том числе. и не только твоих... если компонент делает что-то автоматом, по событиям например от системы, то тоже.
    но лучше, если ты создашь все необходимое в рантайм, прямо в Execute потока, как и все ему возможно необходимое (например для COM необходим вызов Coinitialize, на каждый поток отдельный)
  • Kirill © (24.08.17 10:59) [35]

    > но лучше, если ты создашь все необходимое в рантайм, прямо
    > в Execute потока, как и все ему возможно необходимое (например
    > для COM необходим вызов Coinitialize, на каждый поток отдельный)

    Понял. Сейчас реализую. Спасибо. Видимо придется заменить библиотеку fbembeded на полноценную с файловым хранилищем.Он ведь не будет требовать установленный FBServer на сколько я помню?

    Перечитал пост Юрия:
    1. Убрал
    2. Убрал
    3. фоновый поток только и нужен чтобы отправить данные и все. больше ничего он и не должен делать.
  • Inovet © (24.08.17 11:09) [36]
    > [35] Kirill ©   (24.08.17 10:59)
    > 2. Убрал

    Sleep не убрать надо, а переделать логику, как я 2 раза выше говорил.
  • Kirill © (24.08.17 11:22) [37]

    > как я 2 раза выше говорил.

    Можно тогда для тех кто в танке?

    Основной код пробивает чек на ККТ. Если успешно записывает в БД запись. Дальше:
    1. создается поток и туда передается эта запись?
    2. в уже созданный поток передается эта запись? как образом?
  • Inovet © (24.08.17 11:46) [38]
    > [37] Kirill ©   (24.08.17 11:22)
    > 2. в уже созданный поток передается эта запись? как образом?

    В уже созданный поток.
    Что значит передаётся? Кассовый сохранил, передающий по сигналу от кассового проснулся и забрал. Как я понял, кассовые операции и так хранятся в некой локальной БД, так что в этом случае оба потока к этой БД и обращаются.

    Я говорю, что не надо бессмысленно крутить цикл и грузить этим систему, а сделать правильно через синхронизацию. Как описал выше.
  • Kirill © (24.08.17 11:56) [39]

    > что не надо бессмысленно крутить цикл и грузить этим систему,
    >  а сделать правильно через синхронизацию. Как описал выше.
    >

    Это через команды Resume/Suspend?
  • Inovet © (24.08.17 12:11) [40]
    > [39] Kirill ©   (24.08.17 11:56)
    > Это через команды Resume/Suspend?

    Нет в 20, 21 и ещё далее информация.
  • Kirill © (24.08.17 12:46) [41]
    Упс... Вот эти сообщения пропустил. Сейчас почитаю.
  • Игорь Шевченко © (24.08.17 12:57) [42]
    "Необходимо написать отправку данных в 1С в отдельном потоке. Программа должна делать выборку из БД данных, которые не отправлены и отправлять в 1С. В случае успешной отправки помечать запись как отправленную."


    ConnectToDatabase;
    while not Terminated and HasRecordsToSend do
    begin
     GetRecordToSend;
     if not Terminated then
     begin
       SendRecord;
       MarkRecordAsSent;
     end;
    end;
    DisconnectFromDatabase;


     
    Собственно все. И да, лучше в отдельном потоке, чтобы не мешать основному функционированию.
  • KilkennyCat © (24.08.17 13:03) [43]
    написать отдельное приложение, пущай по таймеру реплицирует енту бд в 1с, раз такие сложности. можно даж красиво в трей запихнуть, с анимацией.
  • Юрий Зотов © (24.08.17 14:05) [44]
    Рискну внести небольшую добавку к коду Игоря.

    ConnectToDatabase;
    while not Terminated and HasRecordsToSend do
    begin
     GetRecordToSend;
     if not Terminated then
     begin
       StartTransaction;
       try
         SendRecord;
         MarkRecordAsSent;
         CommitTransaction;
       except
         on E: Exception do
         begin
           RollbackTransaction;
           WriteErrorToLog(Now, E);
        end;
     end;
    end;
    DisconnectFromDatabase;
  • Kirill © (24.08.17 14:11) [45]
    А здесь снова цикл...

    Я наверно останусь с циклом, т.к. быстрое гугление информации с WaitForSingleObject привело к интересным статьям, но пока не изучил нормально. А текущий код осталось только доработать в плане создание нового подключения к БД и отдельной работы с ней и веб-сервиса.
    Спасибо всем большое. А про WaitForSingleObject почитаю подробнее.
  • Юрий Зотов © (24.08.17 14:15) [46]
    А еще лучше обернуть в try-except не только запись данных, а весь код Игоря, включая коннект к БД. Тогда в лог пропишется и сбой коннекта тоже.

    > ТС

    Вот этот код поместите в Execute. Метод HasRecordsToSend должен ждать либо таймаута (и тогда вернуть False), либо готовности данных для передачи (и тогда вернуть True).
  • Игорь Шевченко © (24.08.17 14:21) [47]

    > Метод HasRecordsToSend должен ждать либо таймаута


    Метод должен определять, согласно условию, что в базе данных есть записи для отправки. Все.
  • Юрий Зотов © (24.08.17 15:04) [48]
    > Игорь Шевченко ©   (24.08.17 14:21) [47]

    И тогда получаем 100%-ную загрузку CPU бесполезным занятием.

    А если HasRecordsToSend ждет (данных или тайм аута), то все получается вполне красиво.

    1. В методе HasRecordsToSend используем WaiForSingleObject с подходящим объектом ядра.

    2. Подготовив данные, главный поток взводит этот объект ядра. При этом второй поток просыпается, пересылает данные и ждет новой порции.

    > ТС

    Connect/Disconnect лучше тоже внести внутрь while - чтобы избежать ненужных коннектов, если данные еще не готовы.
  • ухты © (24.08.17 15:10) [49]

    > Connect/Disconnect лучше тоже внести внутрь while - чтобы
    > избежать ненужных коннектов, если данные еще не готовы.
    как же без конекта он узнает что пора посылать? )
  • Inovet © (24.08.17 15:29) [50]
    > [48] Юрий Зотов ©   (24.08.17 15:04)
    > А если HasRecordsToSend ждет (данных или тайм аута), то
    > все получается вполне красиво.

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

    ИШ, видимо, имел ввиду периодический запуск второго потока без дополнительного объекта синхронизации. Т.е. главный проверяет, не работает ли второй и, если не работает, то запускает его.
  • Inovet © (24.08.17 15:29) [51]
    > [49] ухты ©   (24.08.17 15:10)
    > как же без конекта он узнает что пора посылать?

    Ну вот, приехали.
  • Юрий Зотов © (24.08.17 15:37) [52]
    > [49] ухты ©   (24.08.17 15:10)

    > как же без конекта он узнает что пора посылать?


    Если объект ядра взведен (главным потоком) - значит, пора посылать.
  • Kirill © (24.08.17 16:06) [53]
    Игорь, и Юрий, прошу прощения, но если закончатся записи на отправку цикл завершится.
    Тогда в какой момент надо запускать этот поток? После записи чека в БД, чтобы он отправлялся?
  • Inovet © (24.08.17 16:08) [54]
    > [53] Kirill ©   (24.08.17 16:06)
    > Тогда в какой момент надо запускать этот поток?

    Ты через один пост читаешь?
  • Kirill © (24.08.17 16:16) [55]

    > Ты через один пост читаешь?

    Простите, еще раз... Обновляю. Вижу старые сообщения. Отправляю сообщения - уже куча новых.
  • Leonid Troyanovsky © (24.08.17 17:04) [56]

    > Юрий Зотов ©   (24.08.17 15:04) [48]

    > 1. В методе HasRecordsToSend используем WaiForSingleObject
    > с подходящим объектом ядра.

    Подходящий объект это, видимо, CreateEvent.
    Оный цикл д.б. не внутри HasRecordsToSend, а рядом с Terminated.

    while not Terminated do
       begin
         WaitForSingleObject(EventHasDataToSend, INFINITE);
         GetRecordToSend;
         ..
         SendRecord;
         ..
       end;

    T.е., если в первичном HasRecordsToSend, то он делает Set(Pulse)Event.

    Ну, там еще Destroy дописать.

    Так понятней, IMHO.

    --
    Regards, LVT.
  • Юрий Зотов © (24.08.17 19:48) [57]
    Только не INFINITE (когда данные кончатся, будет зависание).

    if WaitForSingleObject(EventHasDataToSend, число) = WAIT_TIMEOUT then
     continue;
  • Юрий Зотов © (24.08.17 20:16) [58]
    > Kirill ©   (24.08.17 16:16) [55]

    > Обновляю. Вижу старые сообщения. Отправляю
    > сообщения - уже куча новых.


    Дык... Вы же сами этого хотели - жаловались, что не помогают, такие-сякие...
    :o)

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

    Теперь топик, пожалуй, действительно закрыт - все, что было нужно сказать, уже сказано.

    Удачи.
  • Kirill © (25.08.17 09:02) [59]

    > Ваши слова были услышаны - вот и накидали кучу паззлов

    Огромное всем спасибо... Как раз вчера оставшуюся часть дня изучал WaitForSingleObject и пытался понять на какое событие повесить. Спасибо за примеры.
  • Leonid Troyanovsky © (25.08.17 09:45) [60]

    > Юрий Зотов ©   (24.08.17 19:48) [57]

    > if WaitForSingleObject(EventHasDataToSend, число) = WAIT_TIMEOUT
    > then

    Ну, тут не важно какое число, бо корректным должно быть, все же,
    переопределение его Terminate, and so Destroy (т.к. исходный Terminate is static).

    Т.е.:

    procedure TWThread.Terminate; // m.b. virtual;
    begin
      inherited;
      PulseEvent(EventHasDataToSend);
    end;

    destructor TWThread.Destroy; // override;
    begin
      Terminate;
      inherited;
      CloseHandle(EventHasDataToSend);
      ..
    end;


    --
    Regards, LVT.
  • Игорь Шевченко © (25.08.17 10:29) [61]

    > Как раз вчера оставшуюся часть дня изучал WaitForSingleObject
    > и пытался понять на какое событие повесить


    Зачем ?
 
Конференция "Начинающим" » Помогите написать работу с потоком [D7]
Есть новые Нет новых   [118625   +12][b:0.001][p:0.002]