2013-08-07 9 views
1

У меня есть очень старый код (15 + год), который использовался для запуска нормально, на более старых машинах с более старыми версиями программного обеспечения. Теперь это не так хорошо, потому что, если не выполняется условие гонки. Это общий вопрос: скажите мне, почему я должен был знать, и ожидали, что сбой в этом коде, так что я могу распознать рисунок в другом коде:Delphi 7, Windows 7, обработчик событий, код перезапуска

procedure TMainform.portset(iComNumber:word); 
begin 
windows.outputdebugstring(pchar('portset ' + inttostr(icomnumber))); 

with mainform.comport do 
try 
    if open then open := False; // close port 
    comnumber:=iComNumber; 
    baud:=baudrate[baudbox.itemindex]; 
    parity:=pNone; 
    databits:=8; 
    stopbits:=1; 
    open:=true; 
    flushinbuffer; 
    flushoutbuffer; 
    if open then mainform.statusb.Panels[5].text:=st[1,langnum] {Port open} 
     else mainform.statusb.Panels[5].text:=st[2,langnum]; {port set OK} 
except 
    on E: exception do begin 
    windows.OutputDebugString('exception in portset'); 
    mainform.statusb.Panels[5].text:=st[3,langnum]; 
    beep; 
    beep; 
    end; 
end; 
windows.outputdebugstring('portset exit'); 
end; 

Заметим, что flushinbuffer защищен EnterCriticalSection(); AFAIK Ничто другое не защищено, и AFAIK нет секций обработки сообщений. BUT

Когда этот код вызывается из события click, он проходит частично, а затем прерывается событием paint.

Единственная трассировка, которую я сделал, это outputdebugstring. Я могу видеть, что первая строка повторяется при вводе до того, как вторая строка будет показана при выходе. Это реально, или это иллюзия?

Трассировка выглядит следующим образом:

4.2595 [4680] graph form click event 
4.2602 [4680] portset 1 'from click event handler' 
4.2606 [4680] graph form paint event 
4.2608 [4680] portset 1 'from paint event handler' 
4.2609 [4680] portset exit 

4.3373 [4680] portset exit 

Это состояние гонки: краски обработчики события формы вызываются до обработчика событий клика кода заканчивается, что вызывает сбои. Серийный код - AsyncPro. Нет кода нити. Да, есть больше кода, нет, это не ничего особенного делать до «1 подмножестве портов», но это написать в форме, прежде чем он получает там:

with graphform do begin 
    if not waitlab.Visible then begin 
     waitlab.visible:=true; 
     waitprogress.position:=0; 
     waitprogress.visible:=true; 
     waitprogress.max:=214; 
    end; 
end; 
mainform.Statusb.panels[5].text:=gcap[10,langnum]; 

Не сдерживайте: Что она делает неправильно, что я должен искать?

+0

Что такое «событие краски»? И что такое стек вызовов внутри этого события? Весь код работает на потоке gui? –

+0

И что-нибудь вызывает ProcessMessages? –

+0

@DavidHeffernan Просто мысль, но как вы думаете, это может быть изменение в Windows API за последние 15 лет? Принятые с тех пор меры безопасности ОС могут изменить модель потоков, под которой выполняются определенные функции. Просто удар в темноте ... – Sam

ответ

2

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

+0

Это только отчасти верно. Создайте новое приложение форм VCL и поместите на него TButton и TPaintBox. На кнопке нажмите событие вызова PaintBox1.Refresh. Событие paint будет запущено внутри события нажатия кнопки. Вы можете проверить его с помощью отладчика. (XE3) –

+0

Вот почему я попросил дать ясность именно о том, что подразумевается под «событием краски». –

+0

'Refresh()' invalidates, а затем обновляет окно управления, вызывая немедленное сообщение 'WM_PAINT' для доставки в окно без прохождения очереди сообщений. AsyncPro является невизуальным компонентом, и AFAICS не вызывает 'ProcessMessages()' (хотя он и полагается на сообщения) или иначе откатывает очередь сообщений напрямую, предположим, что мое предложение об обработчике событий может быть ошибочно. –

1

Поскольку вы закрытие порта в начале обработчика событий, если есть вероятность инициирования события дважды (т.е. по телефону Application.ProcessMessages в любом из кода, или позвонив по телефону TMainform.portset() непосредственно из рабочего потока), новый экземпляр закроет ваш порт, в то время как старший пытается связаться через него, что приведет к ошибке. AFAIS есть два решения:

  • быстрее, но не менее сносная один, чтобы защитить всю вашу функцию с помощью мьютекса (или события, которое не является объектом syncronisation, но может быть использован как один), но это только скрывает ошибку кодирования, которую вы сделали.

  • более профессиональное решение должно найти, где условие гонки поднято, а затем исправить свой код. Вы можете сделать это, выполнив поиск всех ссылок на Application.ProcessMessages() и TMainform.portset(), и убедитесь, что они не будут вызваны паралельно. Если никакая ссылка не найдена ни на одной из указанных функций, проблема все равно может быть вызвана запуском нескольких экземпляров вашего кода (потому что он не будет создавать несколько COM-портов :)).

+7

TCriticalSection не поможет, когда повторное соединение происходит в том же потоке (путем вызова Application.ProcessMessages). –

+0

@ Уве Раабе - Спасибо, что указали это. (Я знал об этом, но я не смог упомянуть об этом в своем ответе.) – mg30rg

+1

Когда вы получаете что-то не так, и указано, что вы должны сделать, это отредактировать ответ, чтобы исправить его. Даже когда вы это делаете, неверно утверждать, что событие является объектом взаимного исключения. Это не. Однако есть большая проблема, чем все это. Предположим, вы завершите функцию в мьютексе. Затем внутри функции вы вызываете повторный вызов. Теперь ваша нить зашла в тупик. –

4

Это ожидаемое поведение - открытие или закрытие TApdComPort будет обслуживать очереди сообщений, в частности, путем вызова функции, которую он имена SafeYield:

function SafeYield : LongInt; 
    {-Allow other processes a chance to run} 
    var 
    Msg : TMsg; 
    begin 
    SafeYield := 0; 
    if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then begin 
     if Msg.Message = wm_Quit then 
     {Re-post quit message so main message loop will terminate} 
     PostQuitMessage(Msg.WParam) 
     else begin 
     TranslateMessage(Msg); 
     DispatchMessage(Msg); 
     end; 
     {Return message so caller can act on message if necessary} 
     SafeYield := MAKELONG(Msg.Message, Msg.hwnd); 
    end; 
    end; 

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

if Assigned(ComThread) then 
begin 
    {Force the comm thread to wake...} 
    FSerialEvent.SetEvent; 
    {... and wait for it to die} 
    ResetEvent(GeneralEvent); 
    while (ComThread <> nil) do 
    SafeYield; 
end; 

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

0

Remy Lebeau получает ответ на вопрос, потому что, как я и просил, это был общий ответ на общий вопрос. Но это было бы неадекватно без его комментариев в ответ на Уве Раабе.

И что убедительно продемонстрировало, что Реми Лебо был прав, был исключительный ответ от J, указав на конкретную точку, где код не удался.

Спасибо также Дэвиду Хеффернану за вопрос: «Почему код, который отвечает на порт вызова WM_PAINT», который также делает общую точку. И да, быстрое решение заключалось только в том, чтобы заблокировать путь от обработчика события рисования до кода comms, но я сделал это, не признавая более общую точку.

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

Смежные вопросы