1 一个线程中可以去执行其它函数或者创建新的线程
2 线程虽然不能有返回值但是可以用加锁的全局变量实现返回值 百度synchronize
3 线程锁,这个问题有点宽泛,线程所有很多种,临界区,信号量,互斥等等,新手入门建议参考临界区,这个比较简单
InitializeCriticalSection(&cs);//初始化临界区
EnterCriticalSection(&cs);//进入临界区
............//操作代码
LeaveCriticalSection(&cs);//离开临界区
DeleteCriticalSection(&cs);//删除临界区
2 线程虽然不能有返回值但是可以用加锁的全局变量实现返回值 百度synchronize
3 线程锁,这个问题有点宽泛,线程所有很多种,临界区,信号量,互斥等等,新手入门建议参考临界区,这个比较简单
InitializeCriticalSection(&cs);//初始化临界区
EnterCriticalSection(&cs);//进入临界区
............//操作代码
LeaveCriticalSection(&cs);//离开临界区
DeleteCriticalSection(&cs);//删除临界区
http://wenku.baidu.com/view/0bb9df212f60ddccda38a0ee.html
,另外1楼回答的很好,我也是刚解决了线程的问题
示例代码如下(大概是窗体主线程向一个邮件列表里加入待发邮件的信息,由邮件发送线程后台发送邮件):
var
CS:TRTLCriticalSection;
mMailList: TList;
procedure TMailThread.Execute();
begin
while not Terminated do
begin
EnterCriticalSection(CS); //进入临界区
i:=mMailList.Count;
LeaveCriticalSection(CS); //离开临界区
while i > 0 do
begin
//邮件处理代码省略
EnterCriticalSection(CS); //进入临界区
mMailList.Delete(0);
LeaveCriticalSection(CS); //离开临界区
dec(i);
end;
end;
end;procedure TMainForm.FormCreate(Sender: TObject);
begin
//初始化互斥变量
InitializeCriticalSection(CS);
end;procedure TMainForm.FormDestroy(Sender: TObject);
begin
DeleteCriticalSection(CS);
end;//主线程向mMailList里加入新的item的时候也需要如上进入互斥。
//主线程与邮件线程间为并发访问全局变量mMailList
procedure TFMainServer.CRCLANX1Consume(ASender: TObject; const AddrLan,
CardNo: WideString; Mode: Integer; const Data: WideString;
var CanContinue: WordBool; var CardBalance, SubsidyFund: Single;
var IsVerifyPassword: WordBool; var Password: WideString;
var ErrorCode: Integer);
begin
end;
这个为接口提供的消费事件,我如果仿造你给的文库的那个方法,把消费事件里的代码写在一个函数中,用CreateThread(nil,0,@MyThreadFun,nil,0,ID)这种来创建线程,是不是这样写procedure TFMainServer.CRCLANX1Consume(ASender: TObject; const AddrLan,
CardNo: WideString; Mode: Integer; const Data: WideString;
var CanContinue: WordBool; var CardBalance, SubsidyFund: Single;
var IsVerifyPassword: WordBool; var Password: WideString;
var ErrorCode: Integer);
Begin
CreateThread(nil,0,@MyThreadFun,nil,0,ID);
if ErrorCode <> -1 then
begin
CanContinue:= False;
ErrorCode:= ErrorCode;
end;
end;把事件里的变量赋值到公共变量中,然后再在线程里引用,并且最终赋值结果,然后再判断?但是这样,如果我并发触发(多台餐饮机同时刷卡)了CRCLANX1Consume这个接口的事件,他会怎么运行呢
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;type
TForm1 = class(TForm)
Edit1: TEdit;
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Str2: string;
end;
type
TTestThread = class(TThread)
private
Str: string;
List: TMemo;
SL: TStringList;
procedure addToList;
procedure Execute;override;
end;var
Form1: TForm1;implementation{$R *.dfm}procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
th1: TTestThread;
Str1: string;
TSL: TStringList;
Mem: TMemo;
begin
TSL:= TStringList.Create;
Mem:= TMemo.Create(Application);
Mem.Parent:= Self;
Mem.Visible:= False;
th1:= TTestThread.Create(True);
th1.List:= Memo1;
th1.SL:= TSL;
th1.Str:= Str2;
th1.Resume;
ShowMessage(str2);
end;{ TTestThread }procedure TTestThread.addToList;
begin
List.Lines.add(Str);
SL.Add(Str);
end;procedure TTestThread.Execute;
var
i: Integer;
begin
inherited;
for i:= 0 to 10 do
begin
Str:= IntToStr(i);
Synchronize(addToList);
end;
end;
临界区的概念是:当多线程尝试执行某段相同代码时只会有一个线程可以进入临界区,其它线程会等待,所以这里的临界区需要保护的就是你多个线程需要执行的某段相同的代码,明白了?
我不是说showmessage memo1 的也是一样么,那个是后来测试的时候又改了,怎么等待线程结束后再获取呢
你的代码不都写了吗,返回值啥的加到了memo1里面,memo1的onChange事件里写代码,发现memo1的内容变化了,再处理不就行了。
procedure TFMainServer.CRCLANX1Consume(ASender: TObject; const AddrLan,
CardNo: WideString; Mode: Integer; const Data: WideString;
var CanContinue: WordBool; var CardBalance, SubsidyFund: Single;
var IsVerifyPassword: WordBool; var Password: WideString;
var ErrorCode: Integer);
begin
end;
AddLan是餐饮机的IP地址,CardNo是消费的卡号,CanContinue是判断是否能消费,ErrorCode是如果不能消费返回的错误代码,我们在这个事件下查询数据库信息对该卡号在该时间段消费进行判断,比如该卡所属的卡类能否在此餐饮机消费,比如能否在这个时间段消费之类的,如果判断都成立,就赋值CanContinue为true,然后赋值CardBalance(返回的余额),餐饮机就会响应,如果判断不成立,就赋值CanContinue为FALSE,然后赋值ErrorCode,餐饮机就会显示错误代码提示不能消费,原理是这样,但是并发数可能达到40-50这样的,就是40-50台餐饮机同时在这个时间点消费,同时触发该事件,查询数据库用的ADO我现在是在事件执行的时候单独创建,执行完了再释放掉,但是这种方法,当消费数量太大或者其它原因的时候,程序会自动退出导致餐饮机脱机,所以我想能否用线程解决这个问题,但是又必须在该事件下处理和餐饮机的交互~~~
1 如果线程运行完即返回可以使用waitforsingleobject来等待线程结束然后检查公用变量
2 如果线程要循环执行可以使用postthreadmessage来发送消息给主线程,告知返回值已经生成
我写了个小例子,你看下,应该有所领悟的。
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
begin
for i:=1 to 1000 do
begin
Memo1.Lines.Add(IntToStr(i));
Application.ProcessMessages;
end;
end;
第一次按钮按后未执行完,再按一次,把Memo1的输出全选-复制-粘贴 到 记事本里看下,第一次按的被第二次打断了。
所以,该保护的一定要保护,临界区,只要是有可能并发的情况下就要用的。
var
YESL: TStringList;
YETH: TRestaurantThread;
Begin
YESL:= TStringList.Create;
YETH:= TRestaurantThread.Create(True);
YETH.sAddrLan:= '192.168.23.100';
YETH.sCardNo:= '0000200508';
YETH.IsXF:= False;
YETH.SL:= YESL;
YETH.FreeOnTerminate:= True;
YETH.Resume;
while WaitForSingleObject(YETH.Handle,0) <> WAIT_OBJECT_0 do
Application.ProcessMessages;
if YESL.Count <= 0 then
begin
//CanContinue:= False;
//ErrorCode:= 40;
WriteException(FormatDateTime('yyy-mm-dd hh:mm:ss',now())+'查询余额事件线程执行失败!');
end
else
begin
if YESL[3] <> '-1' then
begin
//CanContinue:= False;
//ErrorCode:= StrToIntDef(YESL[3],36);
Exit;
end else
begin
ShowMessage(YESL[2]);
//SubsidyFund:= 0;
end;
end;
end;卡在这句“while WaitForSingleObject(YETH.Handle,0) <> WAIT_OBJECT_0 do
Application.ProcessMessages;”但是我跟踪执行的话,发现线程代码有执行完的说,
以下为线程的代码:
//线程定义
type
TCYThread = class(TThread)
private
IsXF:Boolean;
sAddrLan,sCardNo,sData: WideString;
sCardBalance: Single;
sErrorCode,sMode: Integer;
procedure AddToList();
protected
procedure Execute;override;
public
SL: TStringList;
end;
//AddToList过程
procedure TCYThread.AddToList;
begin
SL.Clear;
SL.Add(sAddrLan);
SL.Add(sCardNo);
SL.Add(FloatToStr(sCardBalance));
SL.Add(IntToStr(sErrorCode));
end;
//Execute代码
procedure TCYThread.Execute;
var
li_CardBalance, li_ConsumeFund: Single;
i, li_Copies, li_Count, n, paIndex,paIndex1,UseNum: Integer;
S: WideString;
EmplID,CYID,SiteID,CardSrtID,RepastID: Integer;
IsPaused,IsAKQ, IsBCard: Boolean;
CardPW,WareNo,SiteKind,CardNum,StrExp: String;
Wareprice,AMoney,LeastMoney,LimitMoney,PMoney,Repastprice: Single;
ValidityDT,XFDate: TDateTime;
ADO_Select,ADO_Select1: TAdoQuery;
BegTime,EndTime,XFTime: TTime;
IsYC,IsXYBC : Boolean;
ADOConnect: TADOConnection;
Begin
inherited;
EnterCriticalSection(Critical1);
sErrorCode:= -1;
FreeOnTerminate:= True;
if IsXF then
begin
try
CardNum :='';
XFDate := StrtoDate(Formatdatetime('yyyy-mm-dd',Date()));
XFTime := StrtoTime(Formatdatetime('hh:mm:ss',Time()));
CardNum := Formatfloat('0000000000',StrtoFloat(sCardNo));
Paindex:=0;
Try
ADOConnect := TADOConnection.Create(nil);
ADOConnect.LoginPrompt := False;
ADOConnect.ConnectionString := 'Provider=SQLOLEDB.1;Password='+password+';Persist Security Info=True;User ID='+UserName+';Initial Catalog='+InitialData+';Data Source='+DataSource+'';
ADOConnect.CommandTimeout := 1800;
ADOConnect.Connected := True;
ADO_Select := TADOQuery.Create(nil);
ADO_Select.Connection := ADOConnect;
except
on e:exception do
Begin
StrExp := FormatDateTime('yyyy-mm-dd hh:mm:ss',now())+' ADOConnect :'+e.Message+'**ErrorCode:36';
if ADOConnect <> nil then StrExp:= StrExp+':'+BoolToStr(ADOConnect.Connected);
if ADO_Select <> nil then StrExp:= StrExp+','+ADO_Select.ConnectionString;
FMainServer.WriteException(StrExp);
sErrorCode :=36;
if ADO_Select <> nil then FreeAndNil(ADO_Select);
if ADOConnect <> nil then FreeAndNil(ADOConnect);
Exit;
End;
End;
//查询判断
except
//错误处理
end;
end;
LeaveCriticalSection(Critical1);
Synchronize(AddToList);
假设
ADOConnect := TADOConnection.Create(nil);
ADOConnect.LoginPrompt := False;
其后一部分出现异常,导致except部分执行,会导致临界区未离开,进入死锁。
临界区是需要用的时候再用,你贴出来的这部分代码似乎只有这一句可能需要互斥
FMainServer.WriteException(StrExp);
http://www.cnblogs.com/andyhere/archive/2008/10/20/1314803.html
Application.ProcessMessages;立即返回,然后判断的啊
TCYThread = class(TThread)
private
mstr: string;
protected
procedure Execute;override;
procedure AddStr;
public
end;var
Form1: TForm1;
mThread: TCYThread;implementation{$R *.dfm}procedure TForm1.Button1Click(Sender: TObject);
begin
mThread := TCYThread.Create(True); mThread.FreeOnTerminate := False;
mThread.Resume; mThread.WaitFor;
mThread.Destroy; showmessage('aa');
end;procedure TCYThread.AddStr();
begin
Form1.Memo1.Lines.Add(mstr);
end;procedure TCYThread.Execute();
var
i: integer;
begin
for i:=1 to 1000 do
begin
mstr := IntToStr(i);
Synchronize(AddStr);
end;
end;
CardNo: WideString; Mode: Integer; const Data: WideString;
var CanContinue: WordBool; var CardBalance, SubsidyFund: Single;
var IsVerifyPassword: WordBool; var Password: WideString;
var ErrorCode: Integer);
var
XFSL: TStringList;
XFTH: TCYThread;
Begin
XFSL:= TStringList.Create;
XFTH:= TCYThread.Create(True,AddrLan,CardNo,Data,Mode,XFSL);
//while WaitForSingleObject(BuTH.Handle,0) <> WAIT_OBJECT_0 do Application.ProcessMessages; //等待线程结束
XFTH.WaitFor;
XFTH.Destroy;
if XFSL.Count <= 0 then
begin
CanContinue:= False;
ErrorCode:= 40;
WriteException(FormatDateTime('yyy-mm-dd hh:mm:ss',now())+'消费事件线程执行失败!');
end
else
begin
if XFSL[3] <> '-1' then
begin
CanContinue:= False;
ErrorCode:= StrToIntDef(XFSL[3],36);
Exit;
end else
begin
CanContinue:= True;
CardBalance:= StrToFloatDef(XFSL[2],0);
SubsidyFund:= 0;
end;
end;
end;
鉴于可能并发刷卡,那我这消费事件是否也要写临界区呢,就是事件前面加进入临界区,事件结束的时候离开临界区,那如果是这样的话,外面的事件操作了临界区,线程里面也操作了临界区,这样会有影响么
function TThread.WaitFor: LongWord;
var
H: array[0..1] of THandle;
WaitResult: Cardinal;
Msg: TMsg;
begin
H[0] := FHandle;
if GetCurrentThreadID = MainThreadID then
begin
WaitResult := 0;
H[1] := SyncEvent;
repeat
{ This prevents a potential deadlock if the background thread
does a SendMessage to the foreground thread }
if WaitResult = WAIT_OBJECT_0 + 2 then
PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
WaitResult := MsgWaitForMultipleObjects(2, H, False, 1000, QS_SENDMESSAGE);
CheckThreadError(WaitResult <> WAIT_FAILED);
if WaitResult = WAIT_OBJECT_0 + 1 then
CheckSynchronize;
until WaitResult = WAIT_OBJECT_0;
end else WaitForSingleObject(H[0], INFINITE);
CheckThreadError(GetExitCodeThread(H[0], Result));
end;
WaitForSingleObject第二个参数是代表等待时间,你0用的不对,这样写的话可是会立即返回的