Выполнение потока «останавливается» при нажатии на кнопку основной формы - PullRequest
2 голосов
/ 21 июля 2011

Я использую Borland C ++ Builder 6. У меня есть приложение с формой.Приложение / основная форма начинает работу.(TThread) Поток создает новый экземпляр сокета сервера и прослушивает данные.Когда поступают данные, поток отображает информацию в главной форме с помощью метода синхронизации.

Проблема заключается в том, что, когда поток отправляет информацию, при нажатии на пункт меню в главной форме выполнение потока временно останавливается.Если я закомментирую Form1->Memo1->Lines->Add(mStr) в методе синхронизации, то есть поток не отправляет информацию в основную форму, поток продолжает выполняться без проблем.Таким образом, данные принимаются и отвечают правильно.

Как только я восстановлю строку и запишу данные в основную форму, и в основной форме будет выбран пункт меню, поток временно остановится.Есть ли способ остановить это поведение, чтобы поток никогда не «блокировался», но все же мог сообщать в основную форму?

После прочтения ответа Мартина вот что я сделал:

inMain.h:

#define WM_ADDLOG (WM_USER+0x0500)

class TForm1: public TForm
{
...
private:
void __fastcall virtual HandleAddLog(TMessage &msg);
...
public:
HWND hWnd;
TStringList *FStringBuf;
__property TStringList *StringBuf={read=FStringBuf,write=FStringBuf};
TCriticalSection tcsMsg;
...
protected:
BEGIN_MESSAGE_MAP
 MESSAGE_HANDLER(WM_ADDLOG,TMessage,HandleAddLog)
END_MESSAGE_MAP(TForm)
}

в Main.cpp:

In TForm1 constructor:
...
  hWnd=FindWindow(NULL,"SoftIEN");
  if(!hWnd)
  {
    exit(0);
  }
  FStringBuf = new TStringList;
  tcsMsg = new TCriticalSection;
...

void __fastcall TForm1::HandleAddLog(TMessage &msg)
{
  String strN,strDateTime,strLine;
  if(Memo1->Lines->Count>10000)
    Memo1->Lines->Clear();
  while(FStringBuf->Count)
  {
    strDateTime = "";
    DateTimeToString(strDateTime, "yy/mm/dd hh:nn:ss.zzz: ", Now());
    strN=FStringBuf->Strings[0];
    FStringBuf->Delete(0);
    strLine=strDateTime + strN;
    Memo1->Lines->Add(strLine);
  }
  TForm::Dispatch(&msg);
}

In Thread.cpp
...
  m_strMsg="Some Message";
  AddLog();
...

void __fastcall TIENServerThread::AddLog()
{
  Form1->tcsMsg->Acquire();
  Form1->StringBuf->Add(m_strMsg);
  Form1->tcsMsg->Release();
  SendMessage(Form1->hWnd,WM_ADDLOG,0, 0);
}

Я также попытался PostMessage в своей функции AddLog.

Все работает нормально,сообщения записываются в заметку, но приложение по-прежнему «зависает», когда я нажимаю на главное меню формы.Любые другие идеи / помощь / примеры?

Спасибо за всю помощь!

Ответы [ 3 ]

2 голосов
/ 10 апреля 2013

SendMessage блокируется, пока оконная процедура фактически не обработает сообщение.Так что это объяснило бы то же поведение, что и с Synchronized.Вы действительно должны использовать PostMessage (или что-то еще, см .: http://msdn.microsoft.com/en-us/library/windows/desktop/ms644950(v=vs.85).aspx), чтобы сделать это асинхронно.

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

Почему бы вам не избавиться от критического раздела, создать строку 'log' в куче в AddLog и отправить ее с помощью параметра wParam сообщения?

2 голосов
/ 21 июля 2011

Я видел это раньше с Delphi TThread.Synchronize, около 25 лет назад.Когда я исследовал, чтобы увидеть, как это работает, я перестал использовать Synchronize () и с тех пор не использовал его (аналогично TThread.waitFor и TThread.OnTerminate).

Использовать PostMessage ().Это больше усилий, чем Synchronize (), часто требующий выделенного класса 'TthreadComms' для переноса данных (создание в потоке, загрузка данных, PostMessage ссылка в lParam, приведение обратно в определяемый пользователем обработчик сообщений, отображение данных, освобождениессылка), но он все еще работает, пока открываются опции модального меню и т. д.

Существует проблема с PostMessage ().Существует небольшое количество операций Windows, которые могут воссоздать окна в вашем приложении, поэтому измените дескриптор формы.Если сообщение отправляется непосредственно в дескриптор формы (самый простой способ опубликовать сообщение в обработчике), существует небольшая, но ненулевая вероятность того, что ОС может воссоздать окно во время операции, поэтому изменение окнасправиться.Это может привести к ошибке PostMessage в этом небольшом временном интервале.Этого можно избежать, но это означает еще большее осложнение.Вы можете создать невидимое окно с помощью API RegisterClass () и CreateWindow () и всегда публиковать сообщения потоков в этом окне, отправляя свои данные в lParam и форму / элемент управления reference в wParam.В WndProc приведите wParam к TControl и вызовите TControl.Perform () для вызова нужного обработчика сообщений.Вам нужно только одно из этих невидимых окон в вашем приложении.В Delphi довольно просто поместить все эти вещи в выделенный модуль и создать окно, создающее материал в разделе инициализации - не уверен насчет C ++ Builder - есть ли в нем разделы инициализации?

Труднееиспользовать PostMessage, но, в отличие от Synchronize (), он работает надежно, независимо от того, что всплыла основная нить пользовательского интерфейса, не блокирует вторичный поток, не был переработан три раза в попытке заставить его работать должным образом и является основнымWindows API, который никогда не исчезнет и не изменится - код, который я написал в D3, все еще работает в D2009.

Rgds, Martin

1 голос
/ 21 июля 2011

Я бы создал простую очередь производителя / потребителя и позволил бы потоку GUI опрашивать его в своем простом обработчике.Затем, когда сетевой поток помещает в него что-то, он может просто отправить фиктивное сообщение в основной поток.

...