多线程同步的经典问题。

B2China 2004-04-23 09:14:57
本机
(1) 接收从客户机发送来的数据,
(2) 接收完毕就和网关进行通信的数据处理,
(3) 处理完毕就发送回应到客户机。
由于本机和客户机的连接时固定的长连接,因此根据(1),(2),(3)可以形成3个线程:
接收线程,数据处理线程,发送回应线程。
请问这3个线程之间怎么实现同步?
...全文
294 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
plbhm 2004-05-05
  • 打赏
  • 举报
回复
学习永无止境,实践是更好的丰富理论的方式.
Kevin_qing 2004-04-30
  • 打赏
  • 举报
回复
设计就不太好,你把线程当函数用吗?为什么要一个线程发送,一个接收,一个处理?
用主线程加工作线程不是更好?
主线程等待client连接,然后为连接的client create工作线程。工作线程负责和客户端及网关的通信和计算等。

如果同时连接的client比较多(1000+)用单线程效率会好些。不过程序上就会比较困难。
举个例子:
client send cmd to server
server 分析cmd和client,然后和gateway通讯(可能有很多次)
server得到最后结果,返回client
先构造session结构(类)保存当前client<->server<->gateway的对话状态
对session来说会有2个socket(client/gateway)
每个socket分r/w状态,说明该session期望读/写数据。

主循环伪码:
while(bRun){
//为了方便,这里用select方式。
fd_set fdr;
fd_set fdw;
FD_ZERO(&fdr);
FD_ZERO(&fdw);
FD_SET(g_sListen,&fdr);//查询连接请求。
for(每个session)
构造fdr/fdw
select...;

for(每个有read/write请求的session)
session[x].run();
}


session::run()根据状态位和参数来决定是读/写哪个socket或者处理数据。
B2China 2004-04-28
  • 打赏
  • 举报
回复
fengyu1907(过河拆桥)
==========================
可以贴出来看一下吗?
fengyu1907 2004-04-28
  • 打赏
  • 举报
回复
呵呵.....
我实现整个框架,只有两百行代码不到.运行起来很流畅.
lostgdi731 2004-04-27
  • 打赏
  • 举报
回复
看了那么多人发的贴后,我觉得还是没有说到一个比较系统的方案。
请你完全地看下去,一定对你有帮助。
其实设计这个东西要注意的问题:如果是有多个客户端的话,如果是比较频繁地进行通讯的话。个人认为最好的办法就是用队列的处理办法(呵呵,我们看windows程序设计的书,为什么不学学人家的思想和方法?)。并且要用到至少两个工作线程,一个是用来接收任何来自客户端的消息(命令和参数)并把这些“合法”的消息压入队列。另一个就是处理消息队列里的客户端命令(这里无论你是做什么工作都最好用一个,因为理论上在同一时间一个CPU只能处理一条指令而已,更复杂的情况估计你没可能遇到)。
这样做的优点很多:首先解决了你为每一次客户端接入就建立并在完成时就关闭哪个线程而耗费的CPU线程切片时间。其二可以解决无论来自多少个客户端的接入(当然你的队列要够大),因为是多线程的原因使得每个客户端有比较尽可能地使到服务端接受来自客户端的命令。其三,这里要解决你所谓的同步的问题就变成关键是解决队列操作的同步问题(这个问题比较简单,比较简单的办法就是让你的队列设为全局的,并通过一些全局标记来允许同时只有有个操作合法,如“压盏”或“出盏”只能在同一个时间有一个为合法)。其四,(这个也应该是三,呵呵)因为无论你创建多少客户端线程都是只有一个能被在执行。

兼容的问题,(这个是对于我的设计来说的,因为我也做过这个,但我不知道我的能不能兼容你的)那就是如果你是用tcp的话,可能在工作线程里出现阻塞。如果是的话,你可以改用UDP,保证数据只要在应用层里加入自己的数据格式就可以了。

最后想说的就是,祝你早日完成,我做这个问题吃过很多亏了。那哪个分就不用给我了(也不多嘛,呵呵),不过你可以发信息给我。或加我的MSN啊。我的代码还保留着,并想和你交换学习一下。
MSN: lostgdi731@hotmail.com
QQ: 56079024
E-mail: lostgdi731@163.com
:) ^V^
B2China 2004-04-27
  • 打赏
  • 举报
回复
大家继续讨论,关于这个话题是最重要的.

分不够,我再开一个100分的帖子,大家进来领分。
B2China 2004-04-27
  • 打赏
  • 举报
回复
框架:
0.绑定socket,accept
1.客户端连接接入.
2.唤醒线程池的一个主处理线程ThreadMain
3.继续接收连接

在主处理线程ThreadMain中:
0.启动ReceiveThread
1.启动ProcessThread;
2.启动SendThread
3.WaitForMultipleObject(...);

但是管理和维护队列以及各个事件之间的同步,共享资源(2个队列)的访问,同步信号的控制
很复杂。
B2China 2004-04-27
  • 打赏
  • 举报
回复
我现在准备为一个连接启动一个主要的线程ThreadMain,在该线程ThreadMain中包含3个线程:
ReceiveThread,ProcessThread,SendThread.并且一个ThreadMain线程对应一个全局的队列,该队列包含了2个子队列:处理队列(ProcessQueue),发送队列(SendQueue).

实现起来好复杂啊!
ray428 2004-04-27
  • 打赏
  • 举报
回复
我在考虑如何把这个问题在Linux C下实现。因为在LINUX C下只有pThread,虽然说功能上差不多,但实际使用的时候和VC下还是有出入的。。。希望大家再讨论讨论。
fengyu1907 2004-04-26
  • 打赏
  • 举报
回复
队列群组的方案:

关键词:用户 Semaphore 队列

1.每个用户包含两个队列:接收数据队列(Queue_ReceiveData),发送数据队列(Queue_SendData);

2.如果采用面向对象编程,则使用Event来控制ReceiveDataThread, DataProcessThread,

SendDataThread的同步(每个用户都有一个Event对象,编程简单,但占用了更多的资源),如果采用

结构化编程,则使用两个Semaphore(g_hsemReceiveData, g_hsemSendData)对象控制所有用户的

相对应的两个队列(编程复杂,占用很少的资源);

3.ReceiveDataThread接收到数据后放入Queue_ReceiveData,增加g_hsemReceiveData信号量.
DataProcessThread从用户Queue_ReceiveData中取得数据并降低g_hsemReceiveData信号量,处理

完后放入用户的Queue_SendData并增加g_hsemSendData信号量.
SendDataThread从用户Queue_SendData中取出数据并降低g_hsemSendData信号量,发送数据;

4.请注意用户数量的控制.最好是"最大信号量 = 用户数量 * 每用户队列长度".


非队列群组的方案:


关键词:用户 Semaphore 队列 数据包

1.两个队列:接收数据队列(Queue_ReceiveData),发送数据队列(Queue_SendData).
两个Semaphore(g_hsemReceiveData, g_hsemSendData)对象控制相对应的两个队列;

2.每个数据包(我称每次接收的数据为一个数据包)都附加了用户信息(即数据来源).

3.与方案1第3步不同之处是:SendDataThread从数据包中读取用户信息(即目标地址),而不是根据

用户队列来判断数据来源和目标地址.

方案2与方案1比较:方案2没有用户数量的考虑和更少的队列操作.

如果数据处理要花时间太多,就可以同时用几个DataProcessThread进行处理.不过得多加几个

Mutex或CriticalSection对象来处理"取数据,存数据"时共享资源(两个队列).

========= 说得对不要忘了给我加分^O^ ==========
fengyu1907 2004-04-26
  • 打赏
  • 举报
回复
如果用队列群组的方式设计,那么用Semaphore替换Event将是很好的选择.
fengyu1907 2004-04-26
  • 打赏
  • 举报
回复
to B2China(海陆空天电磁)
========================
你可以对不同的用户设计不同的队列群组.线程对队列群组进行查询,就象线程调度策略一样.
fengyu1907 2004-04-26
  • 打赏
  • 举报
回复
to B2China(海陆空天电磁)
==========================
对于前一个问题的理解"lovessm(Jensy) | (不知道自己帅不帅)"是正确的,接收数据的线程一直可以保持连接,你后面的队列设计是非常好的,这可我只考虑过你的问题."cvsuser(想当螃蟹的猪头)"的提议也非常好,只是microsoft建议我们在多线程编程时尽量用_beginthreadex,因为这涉及到系统定义的全局变量,CreateThread很可能会影响诸如"__argv"等全局变量,而_beginthreadx对这些全局变量有自己的Copy,不影响别的线程.
B2China 2004-04-26
  • 打赏
  • 举报
回复
还有一个问题,这样把接收数据,处理数据和发送回应到客户端3个阶段分离开来了,会不会造成客户端1返回的回应却是客户端2的回应的错乱情况?也就是返回的回应是混杂的,出现这种情况也是由于线程的并发不定时执行造成的。
B2China 2004-04-26
  • 打赏
  • 举报
回复
学习中
turui 2004-04-26
  • 打赏
  • 举报
回复
简单处理办法,你的用户链表,改成数组,固定的访问,不对用户链表进行删除工作,只设置标志位,那就不用进行同步了。
eliner 2004-04-25
  • 打赏
  • 举报
回复
学习来了
sixibb 2004-04-25
  • 打赏
  • 举报
回复
断不断连接是你的套结子方面的东西了,可以一直连接,也可以处理完后断开

我以前做过一个这样的东西,结构如下:

服务器初始化后,创建一个线程SELECTEVENT等待连接和等待接收信息
一旦有客户端进行连接或者接收到客户端的信息,创建一个线程处理这些操作,
做完或杀死线程

最开始考虑用这种方式是因为我在线程同步上不太懂,到后来发现一直创杀线程其实
是很耗费资源的,并且会形成堆键

后来我又把程序改成一个线程做操作,设置信号通知这个线程是否要去操作了。
也发现一些问题,多客户端的时候。。。。。呵呵,不说也知道了吧。大量的操作还没有
处理完就可能会有另外一个连接请求或者一个连接中的数据操作。。。。。出错拉!!!

到最后才发现服务器端如果是一个大操作的话,单单用一个处理线程是不够的,还要考虑到
哪个线程是否还没有做完事情,是否在等待一些操作。
cvsuser 2004-04-25
  • 打赏
  • 举报
回复
to fengyu1907(过河拆桥)
============================

我个人认为用CreateThread() 或 afxbeginThreadex()创建新的线程取决于的你的程序是调用的c的多线程运行函数库还是microsoft 的多线程运行库。
lovessm 2004-04-24
  • 打赏
  • 举报
回复
>>只是保证和客户端的一次通信(我是这么理解的,不知道对不对?)

错,可以一直保持连接。
while循环只上数据的具体处理,连接之前肯定已经做好。
加载更多回复(7)

15,473

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧