你要在線程中實現消息循環, 自己接收消息要使用:BOOL PostThreadMessage( DWORD idThread, // thread identifier UINT Msg, // message to post WPARAM wParam, // first message parameter LPARAM lParam // second message parameter );
PeekMessage就有像 GetMessage,用来取得消息; 而PostThreadMessage是用来发送消息的。 具体的参数定义、用法参考 Help吧。 PeekMessage的第一个参数 @Msg ,在Delphi里是 TMsg, 你可以这样定义: var MyMsg : TMsg; 下面的代码我瞎写的,请大家指正:////////////////// main.pas ////////////// unit main;interfaceuses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,MyThread; const WM_MyMessage=WM_USER+100; type TForm1 = class(TForm) BtnQuitThread: TButton; ListBox1: TListBox; BtnPost: TButton; BtnStart: TButton; BtnExit: TButton; procedure BtnQuitThreadClick(Sender: TObject); procedure BtnStartClick(Sender: TObject); procedure BtnPostClick(Sender: TObject); procedure BtnExitClick(Sender: TObject); private { Private declarations } public { Public declarations } MyThread : MsgThread; end;var Form1: TForm1;implementation{$R *.DFM}procedure TForm1.BtnQuitThreadClick(Sender: TObject); begin // MyThread.Terminate; if MyThread = nil then exit; if PostThreadMessage(MyThread.ThreadID, WM_QUIT,0,0) then Caption := 'Post Message Ok!' else Caption := 'Post Message Fail!'; end;procedure TForm1.BtnStartClick(Sender: TObject); begin if (MyThread = nil) then MyThread := MsgThread.Create(false); end;procedure TForm1.BtnPostClick(Sender: TObject); begin if MyThread = nil then exit; if PostThreadMessage(MyThread.ThreadID, WM_MyMessage,0,0) then Caption := 'Post Message Ok!' else Caption := 'Post Message Fail!'; end;procedure TForm1.BtnExitClick(Sender: TObject); begin MyThread.Free; Close; end;end. ///////////// MyThread.pas///////////////// unit MyThread;interfaceuses Classes,windows, Messages;const WM_MyMessage=WM_USER+100; type MsgThread = class(TThread) private { Private declarations } FMyString : string; protected procedure Execute; override; procedure ShowString; end;implementation{ Important: Methods and properties of objects in VCL can only be used in a method called using Synchronize, for example, Synchronize(UpdateCaption); and UpdateCaption could look like, procedure PMessage.UpdateCaption; begin Form1.Caption := 'Updated in a thread'; end; }{ PMessage } uses Main;procedure MsgThread.Execute; var Msg : TMsg; begin { Place thread code here } FMyString := 'Thread Start!'; Synchronize(ShowString); while (not Terminated) do begin if PeekMessage(Msg,0,0,0,PM_REMOVE) then begin if (Msg.message = WM_QUIT) then begin FMyString := 'Thread Quit'; Synchronize(ShowString); Terminate; end; if (Msg.message = WM_MyMessage) then begin FMyString := 'Thread Get a USER Message!'; Synchronize(ShowString); end; end; end; end;procedure MsgThread.ShowString; begin Form1.ListBox1.Items.Add(FMyString); end;end.
beyondtkl(大龙驹<暗黑系魔法师&&赏金猎人>)说的“循环发”是正确的,原因是目标线程在一开始建立时,是没有自己的消息队列的,当系统发现它调用GUI相关函数时,才会为该线程建立队列,所以,按微软的建议是用下面类似的代码: while not PostThreadMessage(threadID, WM_NULL, 0, 0) do Sleep(100); 即循环发,直到成功。
>>但我在子线程的EXECUTE 函数中循环检测消息,cpu占有率太高了 可以調低線程的優先級>>因为没有消息时,它还在检测,能有什么办法吗? 所有實現消息循環的程序都是這樣的啊, windows 中有几百上千上萬個類似結構線程在同時運行, 多一個也不算多的
给你一个我刚写的例子 基于 console控制台的 Prj下的program MsgPrj; // // purpose: ReadThread VS WriteThread of one integer array with length 20 // function: // 1. Message Queque in sub-thread <for main-thread auto create msg queue by system> // 2. thread-communication by PostThreadMessage, so the Read/Write // are synchronous // autor: beyondtkl(大龙驹) // date: 2004.11.25 uses Windows, Messages, SysUtils, MsgU in 'MsgU.pas';var nArray: array[0..19] of Integer; hRead, hWrite: THandle; fPost: LongBool; strErr: string; dwErr: DWORD;begin { TODO -oUser -cConsole Main : Insert code here } hWrite := CreateThread(nil, 0, @WriteThread, @nArray, // NULL 0, g_dwWrite); hRead := CreateThread(nil, 0, @ReadThread, @nArray, // NULL 0, g_dwRead); fPost := False; while not fPost do begin fPost := PostThreadMessage(g_dwWrite, WM_CAN_WRITE, 0, 0); dwErr := GetLastError(); if dwErr = 0 then begin fPost := True; end else begin strErr := Format('Error Code Is #%d', [dwErr]); WriteLn(strErr); end; end; Sleep(2000); // let read/write 2 seconds PostThreadMessage(g_dwWrite, WM_QUIT, 0, 0); PostThreadMessage(g_dwRead, WM_QUIT, 0, 0); CloseHandle(hWrite); CloseHandle(hRead); strErr := Format('write times %d, read times %d', [g_dwWriteTime,g_dwReadTime]); WriteLn(strErr); ReadLn; // for user view // in VC can use syste("pause") prevent the console to be closed // but in delphi, i can' got, if someone knows, pls tell me // 3x. end.
2。 msgU.pas // program MsgPrj; // autor: beyondtkl(大龙驹) // date: 2004.11.25unit MsgU;interfaceuses Windows, Messages, SysUtils;const WM_CAN_WRITE = WM_USER + 100; const WM_CAN_READ = WM_USER + 101;function WriteThread(lpVoid: Pointer): DWORD; stdcall; function ReadThread(lpVoid: Pointer): DWORD; stdcall;var g_dwWriteTime: DWORD = 0; g_dwReadTime: DWORD = 0; g_dwRead: DWORD = 0; g_dwWrite: DWORD = 0;implementationfunction WriteThread(lpVoid: Pointer): DWORD; var msg: TMSG; fQuit: Boolean; str: string; i: Integer; pArray: PInteger; begin fQuit := False; while not fQuit do begin while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do begin if msg.message = WM_QUIT then begin fQuit := True; end else if msg.message = WM_CAN_WRITE then begin Inc(g_dwWriteTime); str := Format('write #%d times', [g_dwWriteTime]); WriteLn(str); pArray := PInteger(lpVoid); for i := 0 to 19 do begin pArray^ := i; Inc(pArray); str := Format('write in #%d ', [i]); WriteLn(str); end; PostThreadMessage(g_dwRead, WM_CAN_READ, 0, 0); end else begin TranslateMessage(msg); DispatchMessage(msg); end; end; end; Result := msg.wParam; end;function ReadThread(lpVoid: Pointer): DWORD; var msg: TMSG; pArray: PInteger; nValue: Integer; str: string; i: Integer; begin while GetMessage(msg, 0, 0, 0) do begin TranslateMessage(msg); DispatchMessage(msg); if msg.message = WM_CAN_READ then begin Inc(g_dwReadTime); str := Format('write #%d times', [g_dwReadTime]); WriteLn(str); pArray := PInteger(lpVoid); for i := 0 to 19 do begin nValue := pArray^; Inc(pArray); str := Format('Read in #%d ', [nValue]); WriteLn(str); end; PostThreadMessage(g_dwWrite, WM_CAN_WRITE, 0, 0); end end; Result := msg.wParam; end;end.
UINT Msg, // message to post
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
而PostThreadMessage是用来发送消息的。
具体的参数定义、用法参考 Help吧。
PeekMessage的第一个参数 @Msg ,在Delphi里是 TMsg, 你可以这样定义: var MyMsg : TMsg;
下面的代码我瞎写的,请大家指正:////////////////// main.pas //////////////
unit main;interfaceuses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls,MyThread;
const
WM_MyMessage=WM_USER+100;
type
TForm1 = class(TForm)
BtnQuitThread: TButton;
ListBox1: TListBox;
BtnPost: TButton;
BtnStart: TButton;
BtnExit: TButton;
procedure BtnQuitThreadClick(Sender: TObject);
procedure BtnStartClick(Sender: TObject);
procedure BtnPostClick(Sender: TObject);
procedure BtnExitClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
MyThread : MsgThread;
end;var
Form1: TForm1;implementation{$R *.DFM}procedure TForm1.BtnQuitThreadClick(Sender: TObject);
begin
// MyThread.Terminate;
if MyThread = nil then exit; if PostThreadMessage(MyThread.ThreadID,
WM_QUIT,0,0) then
Caption := 'Post Message Ok!'
else
Caption := 'Post Message Fail!';
end;procedure TForm1.BtnStartClick(Sender: TObject);
begin
if (MyThread = nil) then
MyThread := MsgThread.Create(false);
end;procedure TForm1.BtnPostClick(Sender: TObject);
begin
if MyThread = nil then exit; if PostThreadMessage(MyThread.ThreadID,
WM_MyMessage,0,0) then
Caption := 'Post Message Ok!'
else
Caption := 'Post Message Fail!';
end;procedure TForm1.BtnExitClick(Sender: TObject);
begin
MyThread.Free;
Close;
end;end.
///////////// MyThread.pas/////////////////
unit MyThread;interfaceuses
Classes,windows, Messages;const
WM_MyMessage=WM_USER+100;
type
MsgThread = class(TThread)
private
{ Private declarations }
FMyString : string;
protected
procedure Execute; override;
procedure ShowString;
end;implementation{ Important: Methods and properties of objects in VCL can only be used in a
method called using Synchronize, for example, Synchronize(UpdateCaption); and UpdateCaption could look like, procedure PMessage.UpdateCaption;
begin
Form1.Caption := 'Updated in a thread';
end; }{ PMessage }
uses Main;procedure MsgThread.Execute;
var Msg : TMsg;
begin
{ Place thread code here }
FMyString := 'Thread Start!';
Synchronize(ShowString); while (not Terminated) do
begin
if PeekMessage(Msg,0,0,0,PM_REMOVE) then
begin
if (Msg.message = WM_QUIT) then
begin
FMyString := 'Thread Quit';
Synchronize(ShowString);
Terminate;
end;
if (Msg.message = WM_MyMessage) then
begin
FMyString := 'Thread Get a USER Message!';
Synchronize(ShowString);
end;
end;
end;
end;procedure MsgThread.ShowString;
begin
Form1.ListBox1.Items.Add(FMyString);
end;end.
constructor Create:
Event := CreateEvent(nil, True, False, nil);
execute
postMessage(Form1.Handle, WM_MyDefineMsg, 0, 0);
WaitForSingleObject(Event, INFINITE); //一直等待Form1收到消息
//或直接用SendMessage
SendMessage(Form1.Handle, WM_MyDefineMsg, 0, 0);Form1:
procedure TForm1.MyDefineMsg(var Msg: TMessage);
begin
//访问Thread的属性,如string, Integer,如不在同一个Unit,你将这些线程属性public/published,
SetEvent(Thread.Event); //触发WaitForSingleObject,让它返回,不然那线程会死掉
msg.Result := 0;//控制消息返回值 = Thread.SendMessage返回值
end;
类似这样 不好意思 我写VC的代码了 不过基本上意义都一样DWORD WINAPI ReadThread(LPVOID lpVoid)
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_CAN_READ)
{
cout << "read #" << ++g_dwReadTime << " TIMES " << endl;
int* pArray = (int*)lpVoid;
for (int i=0; i<20; i++)
{
cout << "read in #" << pArray[i] << endl;
}
PostThreadMessage(g_dwWrite,
WM_CAN_WRITE,
0,
0);
}
}
return msg.wParam;
}主线程:
while (!fPost) // 一定要这样循环发 因为如果只发一次的话 可能会发送失败...
{
fPost = PostThreadMessage(g_dwRead,
WM_CAN_Read,
0,
0);
dwErr = GetLastError();
szErr[30] = 0;
if (dwErr != 0)
{
wsprintf(szErr, "MainThread Error ID #%d", dwErr);
cout << szErr << endl;
}
else
{
fPost = TRUE;
}
}
2. 要循环发 知道发送成功...<每次发消息都需要这样>
3. 再发消息之后接着就 ShowMessage(IntToStr(GetLastError())); // 一般是1444号错误
表示你发送的子线程ID无效 或者 子线程没有消息循环...
while not PostThreadMessage(threadID, WM_NULL, 0, 0) do Sleep(100);
即循环发,直到成功。
每個程序都是這樣, 影響不大的, 我想
操作系統會處理的, 這個你不用擔心
谢谢你,但我在子线程的EXECUTE 函数中循环检测消息,cpu占有率太高了,因为没有消息时,它还在检测,能有什么办法吗?
谢谢你,但我在子线程的EXECUTE 函数中循环检测消息,cpu占有率太高了,因为没有消息时,它还在检测,能有什么办法吗?就是这样的呀... 不过应该不会占有很高的占有率吧....
可以調低線程的優先級>>因为没有消息时,它还在检测,能有什么办法吗?
所有實現消息循環的程序都是這樣的啊, windows 中有几百上千上萬個類似結構線程在同時運行, 多一個也不算多的
Prj下的program MsgPrj;
//
// purpose: ReadThread VS WriteThread of one integer array with length 20
// function:
// 1. Message Queque in sub-thread <for main-thread auto create msg queue by system>
// 2. thread-communication by PostThreadMessage, so the Read/Write
// are synchronous
// autor: beyondtkl(大龙驹)
// date: 2004.11.25
uses
Windows,
Messages,
SysUtils,
MsgU in 'MsgU.pas';var
nArray: array[0..19] of Integer;
hRead, hWrite: THandle; fPost: LongBool;
strErr: string;
dwErr: DWORD;begin
{ TODO -oUser -cConsole Main : Insert code here }
hWrite := CreateThread(nil,
0,
@WriteThread,
@nArray, // NULL
0,
g_dwWrite);
hRead := CreateThread(nil,
0,
@ReadThread,
@nArray, // NULL
0,
g_dwRead);
fPost := False;
while not fPost do
begin
fPost := PostThreadMessage(g_dwWrite,
WM_CAN_WRITE,
0,
0);
dwErr := GetLastError();
if dwErr = 0 then
begin
fPost := True;
end
else
begin
strErr := Format('Error Code Is #%d', [dwErr]);
WriteLn(strErr);
end;
end;
Sleep(2000); // let read/write 2 seconds
PostThreadMessage(g_dwWrite,
WM_QUIT,
0,
0);
PostThreadMessage(g_dwRead,
WM_QUIT,
0,
0);
CloseHandle(hWrite);
CloseHandle(hRead); strErr := Format('write times %d, read times %d', [g_dwWriteTime,g_dwReadTime]);
WriteLn(strErr);
ReadLn; // for user view
// in VC can use syste("pause") prevent the console to be closed
// but in delphi, i can' got, if someone knows, pls tell me
// 3x.
end.
// program MsgPrj;
// autor: beyondtkl(大龙驹)
// date: 2004.11.25unit MsgU;interfaceuses
Windows, Messages, SysUtils;const WM_CAN_WRITE = WM_USER + 100;
const WM_CAN_READ = WM_USER + 101;function WriteThread(lpVoid: Pointer): DWORD; stdcall;
function ReadThread(lpVoid: Pointer): DWORD; stdcall;var
g_dwWriteTime: DWORD = 0;
g_dwReadTime: DWORD = 0;
g_dwRead: DWORD = 0;
g_dwWrite: DWORD = 0;implementationfunction WriteThread(lpVoid: Pointer): DWORD;
var
msg: TMSG;
fQuit: Boolean;
str: string;
i: Integer;
pArray: PInteger;
begin
fQuit := False;
while not fQuit do
begin
while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do
begin
if msg.message = WM_QUIT then
begin
fQuit := True;
end
else if msg.message = WM_CAN_WRITE then
begin
Inc(g_dwWriteTime);
str := Format('write #%d times', [g_dwWriteTime]);
WriteLn(str);
pArray := PInteger(lpVoid);
for i := 0 to 19 do
begin
pArray^ := i;
Inc(pArray);
str := Format('write in #%d ', [i]);
WriteLn(str);
end;
PostThreadMessage(g_dwRead,
WM_CAN_READ,
0,
0);
end
else
begin
TranslateMessage(msg);
DispatchMessage(msg);
end;
end;
end;
Result := msg.wParam;
end;function ReadThread(lpVoid: Pointer): DWORD;
var
msg: TMSG;
pArray: PInteger;
nValue: Integer;
str: string;
i: Integer;
begin
while GetMessage(msg, 0, 0, 0) do
begin
TranslateMessage(msg);
DispatchMessage(msg); if msg.message = WM_CAN_READ then
begin
Inc(g_dwReadTime);
str := Format('write #%d times', [g_dwReadTime]);
WriteLn(str);
pArray := PInteger(lpVoid);
for i := 0 to 19 do
begin
nValue := pArray^;
Inc(pArray);
str := Format('Read in #%d ', [nValue]);
WriteLn(str);
end;
PostThreadMessage(g_dwWrite,
WM_CAN_WRITE,
0,
0);
end
end; Result := msg.wParam;
end;end.
PeekMessage()和GetMessage()是有区别的,前者只是瞄一下消息队列,如果有消息就把相关信息放在结构中并返回,如果没有也立刻返回,而后者是必须要等到有消息时才返回。
用GetMessage(),进入这个API后,如果该线程的消息队列中没有任何消息,则该线程立刻休眠(WINDOWS不再为它分配CPU时间),也就是说,GetMessage()这个API不再返回,直到消息队列中有消息,WINDOWS会知道,这个时候才让GetMessage()返回,唤醒线程。这个唤醒工作是不需要你来做的,而是由WINDOWS来完成,而且,如果你非要唤醒该线程,唯一的办法,就是向这个线程丢一个消息。
举例如下://线程入口函数
function MyThread(AParam: Pointer): Integer; stdcall;
var
msg: TMsg;
begin
...
...
GetMessage(msg, 0, 0, 0); //执行到这里时,如果没有消息,
//该API就不会返回,线程也得不到CPU时间
{直到消息队列中有消息,才会执行下面的代码}
...
...
Result := 0;
end;如果你非要有PeekMessage(),那么在循环里必须加上Sleep(),否则CPU占用率会达到100%,Sleep(0)会起到很好的效果。