背景说明:
开发一个目录传输工具,将目录下的文件传输到服务器上。支持多个目录同时传输。限制对文件的传输暂不支持多线程。
目录传输需要进过如下流程,扫描目录形成传输队列,在服务器上创建目录结构,上传文件。流程为串行,文件传输目前也为串行,不支持一个目录下的多个文件同时传输,以及一个文件的多线程传输。
用户可以随时暂停目录的传输,(在任何一个阶段)然后关闭传输程序。在重启程序需要能够恢复到原来传输位置继续传输。对于大文件(可以大于2G)的文件支持断点传输。
类设计的简易视图:
每个Task对应一个要传输的目录。Task有一个Thread负责完成所有的流程。Thread里面运行的是TransFlw的具体实现。如扫描流程,目录上传流程(目录UpFlw),文件上传流程。
TransFlw可以串联形成一个工作流。TransThread中不含有Task的指针,这样TransThread与Task解耦,便于后续将TransThread提出来形成线程池,进行统一管理。TransThread只负责循环执行TransFlw的工作。
do
{
rc = ::WaitForMultipleObjects(2,m_multiHandls,FALSE,INFINITE);
index = rc - WAIT_OBJECT_0;
if ( index == 0 ) //启动一次传输
{
::ResetEvent( m_reqEvent );
while ( m_pCurTransFlw != NULL ){
pnext = m_pCurTransFlw->GetNextFlow();
m_pCurTransFlw->DoCurWork();
if ( m_pCurTransFlw->GetIfStop() )//取消一个流程,需要唤醒在等待的任务
{
m_pCurTransFlw->CallWaitCancelTask();
break;
}
else if ( NULL == pnext ) //执行最后一个流程
{
m_pCurTransFlw->CallWaitFlwFinishTask(); //流程的结束唤醒等待任务
break;
}
else
{
m_pCurTransFlw = pnext;
}
}//end while
}
else //离开信号
{
}
}while(true);
上面代码中TransThread循环调用TransFlw进行执行,知道TransFlw的Next指针为空。下面说明一下这段代码中的需要注意的点。
rc = ::WaitForMultipleObjects(2,m_multiHandls,FALSE,INFINITE);
等待两个Event事件,一个表示启动一次传输,另一个表示退出线程。这样Task只用创建一个线程就可以完成多个流程的操作。
m_pCurTransFlw-> CallWaitFlwFinishTask ();
break;
唤醒等待的Task,任务在执行某一个流程时,如扫描流程时可能需要等待TransThread执行完成才进行后续操作,那么Task就可以等待。就是从TransThrea的唤醒Task中的Event。注意break这样就直接跳出循环,不然在进行while的判断,因为多线程的切换可能会导致,在while判断的时候,m_pCurTransFlw被修改。
下面需要说明的是这一句代码
m_pCurTransFlw->CallWaitCancelTask();
break;
当TransThread在执行一个复杂,耗时的流程时,用户选择了暂停,那么Task所在的主线程直接向m_pCurTransFlw发出退出该流程的信号。但流程的退出不会是实时的,流程中通过的是一个循环判断是否设置了退出位,如果退出位置位那么就退出。这样之间有一个时间差,为了保证线程的同步,那么Task需要再发出退出流程信号后,将自己挂起,等到被唤醒。所以如果线程的退出不是正常那么需要唤醒Task。唤醒后的break;也是避免while的再判断。