线程问题:关于indy的demo/chat的问题。demo中用Tlist来存放客户端的信息。求在多线程中,能正常、安全删除tlist中指定节点的方法。 (51分

  • 主题发起人 主题发起人 talent002
  • 开始时间 开始时间
T

talent002

Unregistered / Unconfirmed
GUEST, unregistred user!
线程问题:关于indy的demo/chat的问题。demo中用Tlist来存放客户端的信息。求在多线程中,能正常、安全删除tlist中指定节点的方法。 (51分)<br />idtcpserver不管用什么方法,要把tlist保护起来,然后删除指定的节点。(就是,在一个线程要对tlist进行删除操作时,其它tlist不能进行删除操作。要等它删除完了才可以让其它线程做删除操作)。
问题:
怎么找出指定的节点?

for i:=0 to tlist.count-1 do
begin
if tlist.items...='...' then
begin
tlist.delete(i);
exit;
end;
end;

这样的方法来找吗?这样的话,如果在找的过程中(找到一半,但还没做删除操作),其它线程对tlist做了删除,或增加,那么tlist.count就会变化。同时也有可能导致tlist.items得不到我想要的那个item。不知怎么避够这个样的错误产生?或是有没其它更好的方法来找到指定的节点?

比如两个线程都在对tlist进行删除操作。
(1)“&lt;0&gt;”表示要删除的节点。“=”表示其它节点。刚开始时
A线程:===================&lt;O&gt;“tlist.count是20”
B线程:===========&lt;O&gt;========
|
时间T

(2)B线程在时间T的位置删除了tlist的一个节点。那么tlist.count就变成了19.很明显线程A再用for i:=0 to tlist.count(这里是还是20) do会出错。
请问怎么解决?
 
等我明天告诉你
 
咳咳...开始讲了,要听的快来啊 ^_^
(开玩笑,水平有限,讲的不对的地方请多包含,欢迎指正,感激!)

关于‘临界区’,实际上他是我们根据需要而规定的。比如:
a:=8;
开始‘临界’
b:=9;
b:=7;
showmessage('yao chu lai le ');
结束‘临界’
d:=6;
我们规定中间三行为临界区,那么当系统中有多个上述程序的
印象,(也就是有多个线程,他们的代码是一样的)则当有一
个线程1执行到临界区以内的的指令,如果有另一个线程2执行
完a:=8; 2就会进入等待状态,当线程1执行完临界区内的指令
后,才可能被唤醒,执行临界区内的指令。上述技术也称‘线程互斥’
上面只是举一个简单例子,实际上不一定要多个线程的代码相同,
不同的代码的线程也可以设置'临界区',实现线程互斥.(也就是互斥访问某对象)

严格来讲,你那种情况应该用‘线程互斥’、而算不上‘线程同步’.
‘线程同步’的典型例子是:生产者程序--&gt;容器--&gt;消费者程序
生产者必须在消费者取走了容器内容后才能放下一个产品,而
消费者必须等生产者放入了产品才能去取(即不能取两次),也就是
两个线程(或进程)需要同步.
也可以讲,互斥是一种同步的特例.

不用临界区实现互斥的原理:
如果你只有两个线程需要互斥,那就设一个全局变量Q:BOOLEAN;
Q=true表示有线程在临界区内,否则没有,代码:
Q:=false;//初始化

线程一:........... 线程一:...........
if Q then 自己的.resume;//挂起自己 if Q then 自己的.resume;//挂起自己
else Q:=true; //占用 else Q:=true; //占用
处理tlist对象的一些语句; 处理tlist对象的一些语句;
Q:=false; //释放 Q:=false; //释放
if 另一个线程.suspended=false if 另一个线程.suspended=false
then 另一个线程.suspended=true; then 另一个线程.suspended=true;
......... .........
但是,虽然程序上没问题,不代表运行时不会出错,假设线程一判断if Q真,还没来及设Q为真
时间片就到了,操作系统转而去另一个线程二,线程二也判断if Q真,结果就会出错.
因为信号量Q本身也遇到了需要互斥访问的问题.(你不要急等我讲完先)

如果有多个线程需要访问,就要复杂一点.先介绍著名的PV操作.
P操作:将信号量减去1,若结果小于1,则将当前线程置为挂起状态
V操作:将信号量加上1,若结果不大于1,则释放一个等待的线程.
具体实现如下:(代码不一定规范,只是说明程序算法思想)
定义全局变量

MS:integer;初始值为1
数组P[],保存所有的等待线程句柄,以备释放.
c:integr;说明数组的成员个数;
(也可以采用单链表保存句柄,节约空间且灵活,但在此不讨论)

procdure P( var S:integer);
begin
s:=s-1; //修改全局变量的值
if s&lt;0 then
begin
挂起当前线程;
将当前线程句柄放入列表中;
end;
end;

procdure V(var S:integer);
begin
s:=s+1; //修改全局变量的值
if s&lt;=0 then
begin
释放一个在表中的线程;
end;

而在程序中这样用:(如果有第二个需要保护的对象可以另外定义一个全局变量)
线程一: 线程二: 线程n:
P(MS); P(MS); P(MS);
访问tlist; 访问tlist; ... 访问tlist;
V(MS); V(MS); V(MS);
end; end; end;

但是,这个程序依然可能出现异常,道理和上面一样,当判断后还没修改就被操作
系统因为时间片或其他原因挂起,再执行其他线程中的pv操作,就会出错.

所以综上所述,判断信号量(MS、Q...)并对其操作的代码没有完全执行完,是不
能中断的,这种不能中断的过程(比如P、V),就称原语,可能是由操作系统提供。

关于在 delphi中实现‘线程互斥'.我查了资料。
1.使用Synchronize,当我们用Synchronize(过程名或方法名等等)可以保证
互斥访问此过程,就是当有一个程序已经调用过程且没有返回主调程序,
别的Synchronize(过程名或方法名等等)就会阻塞.

2.使用VCL对象的Lock方法,在操作控件对象之前,使用其Lock方法,然后
对其进行操作,完成之后,再调用其Unlock方法,释放。
对了,你讲的tlist是控件吗?我好象没找到,你自己试试,有lock方法就可以!

使用WindowsAPI实现在程序中定义临界区
定义一个TRTLCriticalSection类型的变量.(假设是LJQ)
使用之前先初始化,initializeCriticalSection(LJQ);
把你要定义的临界区用EnterCriricalSection(LJQ);
和LeaveCriticalSection(LJQ);括起来即可.
最后要将变量释放,DeleteCritical(LJQ);

至于windows操作系统是否提供象我上面讲的PV原语,不知道!
如何使自己的一段代码在没有执行完之前,不可中断其所在线程,也不知道!
希望知道的朋友告诉我,谢谢您!我的qq:71892967,欢迎交流![:)]
 
到此一游,
 
Synchronize(过程的指针)
这种方法好像不能传递参数?是吗?
 
用TThreadlist代替Tlist它是线程安全的
with threadlist.locklist do
begin
try
for i:=0 to count-1 do
begin
if items...='...' then
begin
delete(i);
exit;
end;
end;
finally
threadlist.unlocklist
end;
end;
 
我看你还是用TTreadList吧,线程用之前LockList,用完后UnLoadList就行了
with MyThreadList.LockList do
try
Remove(obj);
finally
MyThreadList.UnlockList;
end;
 
晕~有人比我快,哈
 
哼,你们讲的,我早就讲了!
 
哈哈,麻子真可爱!
你的回答值得一看!
 
接受答案了.
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
1K
SUNSTONE的Delphi笔记
S
后退
顶部