300高分求多线程问题.不够可以再加分 ( 积分: 300 )

  • 主题发起人 主题发起人 delphi杀手
  • 开始时间 开始时间
D

delphi杀手

Unregistered / Unconfirmed
GUEST, unregistred user!
ServerSocket接收来自客户端的数据.ServerSocket的接收函数是多线程的.在此 函数内将客户端数据赋给一个全局数组.然后写到硬件设备上.现出现的问题是:当 正在写硬件设备时,若此时有另一客户端发来数据,必会改写全局数组内容.那写硬 件设备就会出错.请问如何解决这个问题?
数组不能放到接收函数内做局部数组,因涉及的地方很多.
 
用临界区吧。
//全局变量
g_DataRCS: TRTLCriticalSection;
//初始化临界区
//最好是在程序一开始就这么做
procedure InitDataRCS();
begin
InitializeCriticalSection(g_DataRCS);
end;

//程序退出前删除临界区
procedure DelDataRCS();
begin
DeleteCriticalSection(g_DataRCS);
end;

procedure 线程接收到消息();
begin
//进入临界区,如果此临界区已经被进入则在临界区被Leave前此线程将被挂起
EnterCriticalSection(g_DataRCS);
try
//any code
finally
//离开临界区
LeaveCriticalSection(g_DataRCS);
end;
end;

procedure 向硬件设备写数据();
begin
//进入临界区,如果此临界区已经被进入则在临界区被Leave前此线程将被挂起
EnterCriticalSection(g_DataRCS);
try
//any code
finally
//离开临界区
LeaveCriticalSection(g_DataRCS);
end;
end;
 
to miros:
//进入临界区,绻?肆俳缜?丫?唤?朐蛟诹俳缜?籐eave前此线程将被挂起

thank you ,以上是什么字?怎么乱码? 还有你说的临界区还是跟单线程差不多?就是要前面的用完这个全局变量,后面才能使用吧?
 
1. miros的回复是基本的正解;
2.楼主的问题也很切要害(还是跟单线程差不多?); // 是的, 如果有关键数据要互锁,基本与单线程差不多的.
3.这要看实际的情况, 如果向硬件设备写数据的速度够快过Socket接收数据, 这种简单的临界互锁是没有问题的;
4.如果Socket结束速度可能突发(可能有一阵有大量的数据), 而向硬件设备写数据相对慢,
则要使用中间缓冲区(或者直接将你的全局数组设置成足够大);
5. 使用中间缓冲区也是要双向互锁的, 相对复杂一点;
6. 将全局数组设置成足够大的方法简单一点:
6.1. 定义好 socket读取数据后填写数组的指针; 对数组来说是 w_ptr;
6.2. 定义好 写硬件设备的数据在数组中的指针; 对数组来说是 r_ptr;
6.3. 优化过程:

procedure 向硬件设备写数据(); //本过程也要使用线程执行
var a_var : vartype;
begin
EnterCriticalSection(g_DataRCS);
//在这里通过数组的读写指针获取数据(只按复制的方式获取数组中一个节点数据)
a_var := var_array[r_ptr]; // 这个过程不是这样简单, 比较复杂一点, 要判断数组的头尾,是否有数据准备好了要写...等...
LeaveCriticalSection(g_DataRCS);
// 使用a_var数据写硬件
write_hardware(a_var);
end;
 
建议用队列的方法:
var
Queue:TQueueList;
aRecvData:array of Byte //全局数组
procedure RecvDataFromClient //接收数据
begin
将客户端数据入队
Queue.Push(Data); //Data为接收数据的结构,最好是指针方便插入队列
end;
procedure WriteHardWare //写硬件设备
begin
Data:=Queue.Pop;//出队
aRecvData:=Data;对全局数组赋值
Write(aRecvData);使用全局数组写数据
end;
 
我以前也用SOCKETSERVER 后来发现INDY方便简单。换一换吧。经验之谈
 
關注一下。待分學習學習。。。。。
 
yinzt:
这样也是跟单线程差不多.必须要排队处理每个客户端发来的数据.
 
嘿,这跟单线程绝对有本质的区别!
按照 miros的办法,是这样的流程:
接收一组数据-------->处理一组数据------>再接收一组数据-------->处理理一组数据...
这两个过程是串行,(可以说和单线程差不多)
而按照我上面提出的办法是
线程1: 接收数据--->入队
线程2: 出队---->处理理数据,
这两个过程是并行的,只是多了一个出队入队的操作
一般来说,写硬件的速度肯定比接收数据慢很多,如果客户端突然有一大堆数据发过来,入队以后就可以慢慢处理了,这时不管你再用什么办法处理都是一样的
 
a{
call B;
}

客户端1发来数据执行这个多线程函数A执行到一半即调用函数B .此时另一客户端2发来数据也执行这个多线程函数A .当客户端2启动的多线程函数A也准备执行到函数B时.原来的客户端1引发的线程还在函数B执行中.请问这时程序会怎样处理???
 
用局部数竹啊!
 
用了.但还是不行.所以有上面的提问.有没高手帮我解答一下???

a{
call B;
}

客户端1发来数据执行这个多线程函数A执行到一半即调用函数B .此时另一客户端2发来数据也执行这个多线程函数A .当客户端2启动的多线程函数A也准备执行到函数B时.原来的客户端1引发的线程还在函数B执行中.请问这时程序会怎样处理???
 
0. 不知道楼主对我前面的建议有何看法?
1. 设定一个足够大的数组(缓冲区): Data_Array, 一个锁 Data_Lock;
2. N个Socket接收线程A, 每个线程接收到数据后:
Socket.Receive a data...
Data_Lock.Lock;
Write_data to Data_Array; // 这个处理麻烦一点, 是的, N个线程都要往Data_Array里写数据, 但这里速度飞快, 不用担心, 多线程中的数据锁就是解决这个问题的;
Data_Lock.Unlock;
3. 一个独立的线程B不断的循环从Data_Array中取(读)数据(也要Data_Lock), 写到硬件中;
Data_Lock.Lock;
Get_Data from Data_Array; // 这个处理麻烦一点
Data_Lock.Unlock;
Write Data to your hardware...
4. 并不存在A调用B的说法;
5. 要协调好数据(同步好数据), 要注意几点: 缓冲区(数组)足够大, 数组的读写指针移动问题要处理好(关键就是这里复杂一点点), 如果从Socket接收的数据总量速度>写到硬件设备中的速度, 冗余量>缓冲区的长度时,要考虑其他数据核对机制保证工作的完整性;
6. 从Data_Arrar读写数据速度非常快, 耗时可以忽略不计; 而真正耗时的是Socket接收与写数据到硬件设备中; 所以真正的Data_Lock只在从Data_Array中读写数据时使用,效率很高的.
 
缓冲区或者笨点的办法,生成一个临时文件存放待处理的内容。
 
"函数内将客户端数据赋给一个全局数组"

这个本来就有问题。
 
多谢各位的解答,我也知道全局有问题.所以已改为局部变量.但设缓冲区会有延时.因为客户端发来的数据量很大.光数组长度就要设1000 左右,而且长度不固定.
现在的情况是:主函数A是多线程,子函数B我也想设为多线程.
在函数A的代码内写入 创建 B
创建 B
这样的多线程为何会只运行一个线程?而
创建 B
创建 C
就不会有这问题.
 
// 设备线程
TMyDevice = class(TThread)
private
FDataLock: TCriticalSection;
FDatas: TList;
FDeviceLock: TCriticalSection;
public
procedure WriteDataToDevice(const Data: PChar; Size: Integer);
begin
FDeviceLock.Lock;
// data to device
FDeviceLock.UnLock;
end;
type
TMyData = record
data: PChar;
size: Integer;
end;
procedure WriteData(const Data: PChar; Size: Integer);
var
p: ^TMyData;
begin
P := AllocMem(SizeOf(TMyData) + Size);
p.data := PChar(Integer(P) + SizeOf(TMyData));
Move(Data^, P^.Data, size);
FDataLock.Enter;
FDatas.add(P);
FDataLock.Leave;
end;

procedure Execute; override;
var
P: ^TMyData;
begin
while not Terminated do
begin
if FDatas.Count > 0 then
begin
FDataLock.Enter;
P := FDatas.First;
FDatas.Delete(0);
FDataLock.Leave;
WriteDataToDevice(P.data, p.size);
FreeMem(P);
end else
sleep(1);
end;
end;
end;



MyDevice: TMyDevice;

然后在Clients线程中,直接调用MyDevice.WriteData(...)即可。就不用管了。

简单吧。
 
to errorcode:
执行两次 MyDevice.WriteData(...);
MyDevice.WriteData(...);
可以吗?
 
多线程如何公用一个函数体?
 
多线程如何公用一个函数体?
 
后退
顶部