`
happmaoo
  • 浏览: 4335408 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

基于Sockets的编程中多任务同步的处理机制

阅读更多

目录<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

摘要

多任务同步的场景和问题

利用委托同步的解决方案

利用线程同步解决的方案

两种方案若干问题探讨

摘要:

基于Sockets的网络编程中,由于Sockets的通讯机制是往返的消息发送机制,因此使得单个任务而多个步骤(每个步骤也可以称作一个小的任务)的完成必定依赖于或者取决于前导的任务,因此编程处理的异步性就体现出来。而使得这些相互连贯的或者有联系的任务得以按照预定的定义流程去执行,很重要的一个关键问题就是如何去同步。采用何种同步的机制,实质上是采用什么样的技术路线去协调多个任务,当然任何一种机制实现同步并非适用于任何的场合,关键还是看需求。本文将在给出多任务同步的一些基本概念以及场景、问题,然后提出采用线程同步和委托的方式实现同步,并做出探讨,指出其存在问题和适用之处。

1 多任务同步的场景和问题

11 多任务中涉及到的一些概念

任务:逻辑上完成一步操作或者多步操作的集合,这个概念很泛,原则上没有严格的要求去定义它,需要读者自己去理解。

当前任务:正在执行的任务;

前导任务:当前任务在开始之前必须完成的任务。这实际上是一种前后的关系,不仅前导任务完成之后,后面一个任务才能执行,而且当前任务如何去做的逻辑取决于任务前导任务完成的状态。

后继任务:当前任务完成之后启动的任务。当前任务就是前导任务的后继任务。

12 多任务的一个场景和其中的问题

在笔者参与的一个项目中,有这样的一个需求,用于分布式的文件存储服务:

在这个系统里,有多个客户机和一台服务器,每个客户机贡献出一定的磁盘空间组成网络上虚拟磁盘的一部分,因此具体的文件数据是存储在客户机上,而服务器只存储关于文件的信息,如目录的信息,文件的信息(文件名、大小、拥有者、文件的分布情况)。因此用户上传到该系统的文件的物理存储位置实际上是分布在若干台客户机上(每台客户机上是该文件的一个分块)。那么当用户请求下载该文件得时候,需要从所有存储有该文件的分块的机器上下载所有的分块,然后在本机合并为原文件。假如当前有一个文件名为A.rar的文件被分割为3个部分(分割为A.rar.1,A.rar.2,A.rar.3)分别存储在不同的机器(X,Y,Z)上。现在有一台机器H现在希望下载A.rar文件,那么实际上它将从X机器上获得A.rar.1,然后从Y机器上获得A.rar.2,最后从Z机器上获得A.rar.3然后到本机合并成A.rar,可以用流程图表示如下

这三次传输就可以称为一次任务,而这个任务中又包含了三个子任务,而且这三个子任务是否执行是有关系的,假设当前任务是和Y传输A.rar.2文件,之所以能够执行到该任务,必须是在前导任务(和X传输A.rar.1文件)成功的前提下,当然如果当前任务如果执行不成功的话,那么后继任务(和Z传输A.rar.3文件)必然无法开始。很显然这三个任务之间存在着一个同步的问题。

2.利用委托同步的解决方案

当笔者在设计过程中遇到上述问题,笔者想到了使用委托来同步这三个子任务,也就是说在当前任务也就是往X机器传输A.rar.1完成,通过委托回调执行后继任务也就是往Y机器传输A.rar.2任务。先给出文件传输函数:

public void ReadFileData(System.Net.Sockets.Socket sock,string filename,long filesize ,MessageEventHandler downLoadFileCall)

{

FileStream fout = new FileStream(filename, FileMode.Create, FileAccess.Write) ;

//根据文件名将文件读成文件流

NetworkStream nfs = new NetworkStream(sock) ;

//绑定网络流到特定的Socket

long size=filesize ;

long rby=0 ;

try

{

while(rby < size)

//利用判断传输的长度是不是总长度来传输整个文件

{

byte[] buffer = new byte[32767] ;

int i = 0

try

{

i = nfs.Read(buffer,0,buffer.Length) ;

//将文件流中的特定长度读入缓存。

}

catch(Exception e)

{

string temp = e.ToString();

}

fout.Write(buffer,0,(int)i) ;

//将缓存写入网络流

rby=rby+i ;

ShowProgress(filename+"|DownLoad|"+(rby*100/size));

Thread.Sleep(100);

}

nfs.Flush();

nfs.Close();

nfs = null;

fout.Flush();

fout.Close() ;

downLoadFileCall("DownLoadFileOK");

//上面通过回调执行下面的任务

return;

}

//异常处理

catch(Exception ed)

{

fout.Flush();

fout.Close() ;

this.ViewMessage("A Exception occured in file transfer"+ed.ToString());

}

}

可以看出这个函数的参数表中有一个委托参数downLoadFileCall,同时可以看到在整个文件传输完成的时候调用该委托。下面我们再来看一下这个函数是如何被调用的以及该委托回调的是什么函数:

private void DownSingleFile(string state)

{

if(hustfile != null)

{

hustfile.Dispose();

hustfile = null;

}

SpliteFileInfo currentFile=(SpliteFileInfo)down_FileList[FileIndex];

hustfile = new FileSocket(currentFile.LocationIP,currentFile.UserPort,new Callback(this.ViewMessage),new Callback(this.ViewProcess));

hustfile.Connect();

Thread.Sleep(1000);

if(this.FileIndex<this.down_FileList.Count-1)

{

FileIndex++;

hustfile.GetFile(currentFile.FileName,currentFile.Size,new Callback(this.DownSingleFile));

}

//最后一个文件下在完毕后应该恢复文件

else

{

hustfile.GetFile(currentFile.FileName,currentFile.Size,new Callback(this.RecoverFile));

}

}

public void GetFile(string filename,long filesize,Callback FileDownOver)

{

Socket sock=client_socket;

if(sock!=null)

{

WriteLog("请求下载文件");

this.DownloadFile = FileDownOver;

string cmd="GetFile:";

string Msg=cmd+filename;

this.filename=filename;

this.filesize=filesize;

sender = System.Text.Encoding.ASCII.GetBytes(Msg) ;

sock.Send(sender);

WriteLog("文件下载开始");

(new FileTransfer(ShowProcess)).ReadFileData(sock,FileName,filesize,DownloadFile);

}

}

我们可以关注一下DownSingleFile这个函数中IF-ELSE语句,在判断条件成立的时候,都是调用GetFile函数,同时将DownSingleFile函数自身作为回调函数传入到GetFile中,而在GetFile函数,它将这个传入的委托直接传给了前面给出的ReadFileData函数。这表示在前面两个任务的时候都是ReadFileData在一个任务完成的时候回调DownSingleFile执行下一个传输任务。同时我们可以看到当到达A.rar.2传输完成的时候,回调DownSingleFile此时将执行ELSE中的语句此时传给GetFile,然后由GetFile传给ReadFileData的回调函数将不再是DownSingleFile而是一个叫做RecoveFile,很明显在我们所有的传输任务完成后,下一步将是合并这个文件。

笔者在解决三个子任务的协调的时候是将当前任务的是否执行的决策交给了前导任务,通过委托回调的方式来实现。当前导任务完成后就回调执行下一个任务。当然相同的是后继任务的决策交给了当前任务。只有在当前任务完成的情况下,才回调执行后继任务。

3.利用线程同步解决的方案

在遇到上面的问题的时候,当然也可以使用线程同步的方法来实现。先看看下图:

图中主线程函数顺序和X,Y,Z传输相应的文件,每次启动文件传输线程后,主线程阻塞。而监听线程是在监听两台主机之间的命令传输,当监听线程接收到对方传来的文件传送完毕的消息后就唤醒主线程继续往下执行,其函数实现在此我用伪代码表示如下:

主线程:

首先要定义一个用于线程同步的对象m_Sync.

private ManualResetEvent m_Sync=new ManualResetEvent(false);

接下来就是主线程中负责文件传输的代码。

MainThread()

{

filename = ………;//获得需要传输的文件名。

fileSocket = ………;//生成和X传输的套接子。

(new FileTransfer).transfer(fileSocket,filename,);//X机器之间传输文件

m_Sync.WaitOne();

filename = ………;//获得需要传输的文件名。

fileSocket = ………;//生成和Y传输的套接子。

(new FileTransfer).transfer(fileSocket,filename,);//Y机器之间传输文件

m_Sync.WaitOne();

filename = ………;//获得需要传输的文件名。

fileSocket = ………;//生成和Z传输的套接子。

(new FileTransfer).transfer(fileSocket,filename,);//Z机器之间传输文件

RecoverFile();//恢复整个文件

}

再来关注一下监听线程中如何唤醒阻塞的主线程的。

因为笔者在设计整个系统的时候,有一个专门的监听线程一直负责两端的通讯,在该线程接收到对方文件传输完成的命令后,它将会去调用一个m_Sync.Set()来唤醒被阻塞主线程。从而使得主线程继续下一个任务。用以上的方法也是可以实现了多个任务之间同步的。

4.两种方案若干问题探讨

我们纵观上面的两种方法都是在我们事先确定三个子任务的执行为串行的时候,所采取的解决方案。而我们在解决这个问题的时候,使用串行的传输是不是我们的唯一或者是最优的解决方案呢。答案显然是不一定的,我们也可以使用并行的处理方式,就是三次传输文件任务,我们可以选择并发,这样当三次传输过程都不存在问题的时候,显然要比我们使用的串行的方式来的优越,但是并发的时候遇到网络问题或者其他问题,我们的某个传输不成功,那么理论上说,另外两个传输将不能继续执行下去。也就是说这个里面也有一个多任务的同步问题,这个问题如何去解决,在此不再去讨论。


分享到:
评论

相关推荐

    Windows Sockets网络编程 可能是最清晰版本(Windows Sockets 2规范解释小组负责人亲自执笔。)总共4个包,part1

    《Windows Sockets网络编程》是WindowsSockets网络编程领域公认的经典著作,由Windows Sockets2.0规范解释小组负责人亲自执笔,权威性毋庸置疑。它结合大量示例,对WindowsSockets规范进行了深刻地解读,系统讲解了...

    Windows Sockets网络编程 总计4个包,part2

    《Windows Sockets网络编程》是WindowsSockets网络编程领域公认的经典著作,由Windows Sockets2.0规范解释小组负责人亲自执笔,权威性毋庸置疑。它结合大量示例,对WindowsSockets规范进行了深刻地解读,系统讲解了...

    Visual C++_Turbo C 串口通信编程实践.(电子工业.龚建伟.熊光明) 第二版 电子版

    9.5.2 利用Windows Sockets API和第三方提供的类进行编程 262 9.6 串口通信用于遥控操作简例 262 第10章 计算机串口与其他设备通信编程实例 266 10.1通过串口收发短消息 266 10.1.1 SMS编码规范及编码与解码例程 266...

    Visual C++/Turbo C串口通信编程实践及源代码-3

    9.5.2 利用windows sockets api和第三方提供的类进行编程 262 9.6 串口通信用于遥控操作简例 262 第10章 计算机串口与其他设备通信编程实例 266 10.1通过串口收发短消息 266 10.1.1 sms编码规范及编码与解码例程...

    Visual C++/Turbo C串口通信编程实践及源代码-2

    9.5.2 利用windows sockets api和第三方提供的类进行编程 262 9.6 串口通信用于遥控操作简例 262 第10章 计算机串口与其他设备通信编程实例 266 10.1通过串口收发短消息 266 10.1.1 sms编码规范及编码与解码例程...

    Visual C++/Turbo C串口通信编程实践 及源代码-1

    9.5.2 利用windows sockets api和第三方提供的类进行编程 262 9.6 串口通信用于遥控操作简例 262 第10章 计算机串口与其他设备通信编程实例 266 10.1通过串口收发短消息 266 10.1.1 sms编码规范及编码与解码例程...

    Visual C++_Turbo C 串口通信编程实践.(电子工业.龚建伟.熊光明) 源码光盘

    9.5.2 利用Windows Sockets API和第三方提供的类进行编程 262 9.6 串口通信用于遥控操作简例 262 第10章 计算机串口与其他设备通信编程实例 266 10.1通过串口收发短消息 266 10.1.1 SMS编码规范及编码与解码例程 266...

    Visual Basic.NET线程参考手册

    第1章 定义线程 1.1 线程的定义 1.1.1 多任务 1.1.2 进程 1.1.3 线程 1.2 Visual Basic.NET对线程的支持 1.2.1 System.AppDomain类 1.2.2 线程管理与.NET运行库 1.3 本章小结第2章 .NET中的线程 2.1 System....

    windows 程序设计.doc

    20. 多任务和多线程 1054 多任务的各种模式 1054 WINDOWS的多线程处理 1058 线程同步 1080 事件信号 1081 线程区域储存空间(TLS) 1091 21. 动态链接库 1092 动态链接库的基本知识 1092 各式各样的DLL讨论 ...

    Visual C++2010开发权威指南(共三部分).part1.rar

    7.5.2 鼠标的消息处理机制 373 7.5.3 示例 374 7.6 创建启动界面 376 7.7 创建特效窗口启动应用程序 378 7.8 创建特效窗口关闭应用程序 378 7.9 小结 383 第8章 Visual C++ 2010 MFC文本与字体 384 8.1 CFont字体类...

    UNIX 高级教程系统技术内幕

    3.6 Solaris 和 SVR4 的多线程处理 3.6.1 内核线程 3.6.2 轻量级进程的实现 3.6.3 用户线程 3.6.4 用户线程的实现 3.6.5 中断处理 3.6.6 系统调用处理 3.7 Mach 中的线程 3.7.1 Mach 的抽象概念——任务和线程 3.7.2...

    windows程序设计第五版 chm 版本

    《Windows程序设计(第5版 珍藏版)》是一本经典的Windows编程圣经,曾经伴随着近50万Windows程序员步入编程殿堂,成长为IT时代的技术精英。 作为Windows开发人员的必备参考,涵盖基础知识和中高级主题,全面地介绍了...

    Windows程序设计

    …………………… 多任务的各种模式 …………………… WINDOWS的多线程处理 …………………… 线程同步 …………………… 事件信号 …………………… 线程区域储存空间(TLS) 21. 动态链接库 ………………...

    网络驱动程序设计指南

    2.4.1.1 在中间层驱动程序中实现ProtocolReceivePacket处理程序 232 2.4.1.2 在协议驱动程序中实现ProtocolReceive处理程序 233 2.4.1.3 从面向无连接协议驱动程序中访问OOB数据信息 234 2.4.2 面向连接协议驱动程序...

    ASP.net技术内幕

    ADO.NET简介 &lt;br/&gt;9.1 ADO.NET概述 9.2 执行常见的数据库任务 9.2.1 打开数据库链接 9.2.2 从数据库表获取记录 9.2.3 在查询中使用参数 9.2.4 向数据库添加记录 9.2.5 更新...

    asp.net技术内幕(1)

    15.2 使用应用程序状态 15.2.1 理解应用程序状态和同步 15.2.2 使用Global.asax文件 15.2.3 理解Context和使用Global.asax文件 15.2.4 处理应用程序Start和Init事件 15.2.5 处理Application...

    asp.net技术内幕(2)

    15.2 使用应用程序状态 15.2.1 理解应用程序状态和同步 15.2.2 使用Global.asax文件 15.2.3 理解Context和使用Global.asax文件 15.2.4 处理应用程序Start和Init事件 15.2.5 处理Application...

Global site tag (gtag.js) - Google Analytics