云打包app网站源码分享?云打包apk

大家好,今天小编来为大家解答以下的问题,关于云打包app网站源码分享,云打包apk这个很多人还不知道,现在让我们一起来看看吧!

云计算服务软件基础框架的构建

前面提到,构建了云计算服务软件的基础框架后,开发人员在开发新的云计算服务软件时,只需要根据具体任务修改“任务执行”模块即可,而通用模块是可以直接复用的。

本节将介绍具体的构建方法,主要从4个方面进行深入讲解,分别是进程与线程、线程的同步与互斥、线程模型和软件结构。

进程与线程

云计算服务软件在运行期间需要同时做多件事情,例如,在执行任务的同时需要监听任务变更指令。而一个软件想要同时做多件事情,则必然会涉及多线程。本小节先介绍一下进程与线程的相关内容。

注意:进程与线程的相关内容在不同操作系统或不同版本系统内核上会有所区别,但一般是大同小异的。本节以CentOS操作系统为例(Linux内核的操作系统都适用)。

进程是软件运行时的实例,进程拥有独立的系统资源,一般来说,进程与进程间是互不影响的。运行一个软件时,系统会产生一个进程,同一软件被同时运行多次的话,则系统会产生多个进程,而这多个进程虽然是执行相同的任务,但它们是互不影响的。当然,一个程序也可以创建多个进程(子进程)以实现并行执行多任务的目的。

线程是进程中的实际运作单位,进程是线程的容器,一个进程包含一个或多个线程,多个线程可并行执行不同的任务。同一进程中的多个线程共享该进程的全部系统资源(如虚拟地址空间、文件描述符、信号处理等),但每个线程都有各自的调用栈、寄存器环境及线程本地存储等。同一进程的多个线程可共用部分数据,如全局变量、堆空间的变量等。线程的相关操作如代码5.1所示。

代码5.1线程的相关操作

include<pthread.h>//线程相关的头文件

pthread_mutex_tmutexLock;//定义互斥锁,一般为全局变量或静态变量

mutexLock=PTHREAD_MUTEX_INITIALIZER;//初始化互斥锁

//对互斥锁进行上锁,如果已经上锁,当前线程会被阻塞

pthread_mutex_lock(&mutexLock);

//上锁和释放互斥锁期间,

//确保中间的操作不受其他线程影响

pthread_mutex_unlock(&mutexLock);//释放互斥锁

使用互斥锁需要尽量避免死锁的情况,发生死锁后,可能会导致多个线程永远处于等待状态。一般出现死锁状态有两种情况,一是线程对同一个互斥锁连续加锁两次;二是线程涉及多个互斥锁时也可能会发生死锁,例如,线程1锁住第一个互斥锁且等待第二个互斥锁的同时,线程2锁住第二个互斥锁且等待第一个互斥锁。对于第一种情况,一般是一些不必要的人为错误;针对第二种情况,可以通过控制对多个互斥锁加锁和解锁的顺序解决。但有些时候,由于复杂的程序结构使得对多个互斥锁的加锁和解锁进行排序是困难的,此时可尝试使用pthread_mutex_trylock()函数对互斥锁进行加锁,如果互斥锁已经上锁,则此函数会返回失败,线程不会被阻塞。

注意:线程同步在现实编程中是非常复杂的,而死锁是其复杂性的表现。但是,不要把死锁问题放大,从而使用一些奇怪的方法实现线程同步。

2.条件变量

条件变量是线程同步的另外一种机制,条件变量可用于阻塞线程,直到某种条件成立。使用条件变量主要包含两个动作,一个是线程等待“条件变量被触发”而被阻塞;另一个是线程触发“条件成立”(唤醒被阻塞的线程)。使用条件变量能替代“不断轮询条件是否成立”等浪费性能的做法。一般情况下,条件变量需要与互斥锁结合使用,作为互斥锁的补充。

条件变量实际上是一个线程被唤醒的变量,而线程是否进入等待状态,则需要根据其他条件来判断。使用条件变量和互斥锁实现线程同步的流程如图5.11所示。其中,线程1由于依赖数据未产生而进入等待状态(等待条件变量触发),线程2产生线程1依赖的数据后触发条件变量(唤醒线程1,由于线程2占用互斥锁,所以线程1继续处于等待状态),当线程2释放互斥锁后,线程1对互斥锁加锁并继续运行。

图5.11使用条件变量和互斥锁实现线程同步

条件变量和互斥锁的相关操作如代码5.3所示,其中,一般使用while(而不是if)判断是否进入等待状态,因为等待条件变量触发的线程可能不止一个,但条件变量触发时可能会唤醒多个等待条件变量触发的线程,使用while可以让被唤醒的线程重新判断进入等待状态的条件,避免由于其他线程消耗了等待的数据而发生错误。当然,如果确保等待的线程只会有一个,则可以用if判断是否进入等待状态。

说明:一般情况下,函数pthread_cond_signal()只会唤醒等待该条件变量的某个线程,但其内部为了简化实现,可能会唤醒不止一个线程。另外,函数pthread_cond_broadcast()可以唤醒等待该条件变量的所有线程。

代码5.3条件变量和互斥锁的相关操作

include<pthread.h>//线程相关的头文件

//定义互斥锁和条件变量,都为全局变量

pthread_mutex_tmutexLock;//定义互斥锁

pthread_cond_tcondProducer;//定义生产者被唤醒的条件变量

pthread_cond_tcondConsumer;//定义消费者被唤醒的条件变量

//初始化以上变量

mutexLock=PTHREAD_MUTEX_INITIALIZER;

condProducer=PTHREAD_COND_INITIALIZER;

condConsumer=PTHREAD_COND_INITIALIZER;

//定义数据仓库相关变量,都为全局变量,这里为了方便描述,以数组为例

include<pthread.h>//线程相关的头文件

include<pthread.h>//线程相关的头文件

include<vector>//容器相关的头文件

//定义状态的枚举

enumSTATE{

NONE=0,

WAIT,

ACTIVE

};

//定义状态变量,此变量为全局变量

STATEstate=WAIT;

//定义进度变量,此变量为全局变量,这里以JSON变量为例

Json::Valueprogress;

//定义互斥锁和条件变量,都为全局变量

pthread_mutex_treportLock;//定义汇报线程与任务执行线程的互斥锁

//初始化以上变量

reportLock=PTHREAD_MUTEX_INITIALIZER;

//状态变量修改,任务执行线程调用

//任务执行线程在任务开始或任务结束时调用

//任务结束时,需要清空进度变量

voidUpdateState(STATEdate){

//对互斥锁进行上锁

pthread_mutex_lock(&reportLock);

//修改状态变量

state=data;

If(data==WAIT){

//清空任务变量

if(!progress.empty()){

progress.clear();

}

}

//释放互斥锁

pthread_mutex_unlock(&reportLock);

}

//状态变量获取函数,汇报线程调用

//汇报线程定时调用

STATEGetState(){

STATEdate;

//对互斥锁进行上锁

pthread_mutex_lock(&reportLock);

//获取状态变量

date=state;

//释放互斥锁

pthread_mutex_unlock(&reportLock);

returndata;

}//进度变量修改函数,任务执行线程调用

//任务执行线程在执行任务过程中调用

voidUpdateProgress(Json::Valuedate){

//对互斥锁进行上锁

pthread_mutex_lock(&reportLock);

//修改状态变量

progress=data;

//释放互斥锁

pthread_mutex_unlock(&reportLock);

}

//进度变量获取函数,汇报线程调用

//汇报线程定时调用

Json::ValueGetProgress(){

Json::Valuedate;

//对互斥锁进行上锁

pthread_mutex_lock(&reportLock);

//获取进度变量

date=progress;

//释放互斥锁

pthread_mutex_unlock(&reportLock);

returndata;

}

(3)变更指令获取线程与任务执行线程的同步。“变更指令获取线程”监听“指令池”并获取任务变更指令,若任务变更指令与当前任务ID匹配,则加入变更“指令队列”中,之后,“变更指令获取线程”继续监听指令池;

“任务执行线程”在执行任务过程中不断获取任务变更指令并响应指令。变更指令获取线程与任务执行线程的同步流程如图5.16所示。

图5.16变更指令获取线程与任务执行线程的同步流

注意:任务执行线程的主逻辑一般都是循环,任务执行线程需要在每个循环周期都查询变更指令。另外,任务执行线程不一定能及时处理变更指令,可能会造成变更指令积压的情况,为了应对这样的场景,变更指令需要放到队列当中。

在程序中,变更指令获取线程与任务执行线程的同步需要两个函数,分别用于变更指令增加和变更指令获取。变更指令获取线程与任务执行线程同步的代码实现如代码5.7所示,其中,变更指令变量一般为JSON类型的变量,这样能灵活设置指令参数。

代码5.7变更指令获取线程与任务执行线程同步的代码实现

include<jsoncpp/json/json.h>//JSON类型相关的头文件

#include<queue>//队列的相关头文件

//定义指令变更队列,此变量为全局变量

Json::Valueprogress;

std::queue<Json::Value>=missionChangeQueue;

//定义互斥锁和条件变量,都为全局变量

pthread_mutex_tchangeLock;//定义变更指令获取线程与任务执行线程的互斥锁

//初始化以上变量

changeLock=PTHREAD_MUTEX_INITIALIZER;

//变更指令增加函数,变更指令获取线程调用

//需要先判断变更指令是否与正在执行的任务ID相匹配

voidPushMissionChange(Json::Valuedate){

//对互斥锁进行上锁

pthread_mutex_lock(&changeLock);

//向指令变更队列添加变更指令

missionChangeQueue.push(date);

//释放互斥锁

pthread_mutex_unlock(&changeLock);}

//变更指令获取函数,任务执行线程调用

//任务执行线程在任务执行过程中不断查看变更指令

Json::ValueGetMissionChange(){

Json::Valuedate;

//对互斥锁进行上锁

pthread_mutex_lock(&changeLock);

//获取变更指令

date=missionChangeQueue.front();

//清除队列中已获取的变更指令

missionChangeQueue.pop();

//释放互斥锁

pthread_mutex_unlock(&changeLock);

returndata;

}

软件结构

在明确了线程模型后,云计算服务软件的骨架也就明确了。在这骨架之上,还需要补充一些通用部分,如错误码文件、通用函数文件、配置文件及日志配置文件等。除此之外,建议把云计算服务软件分成两层,即主逻辑层和模块层,这样能让软件结构更加清晰。云计算服务软件的基础结构如图5.17所示。其中,为了方便管理,线程同步的相关函数及变量都统一放到了同一个地方(线程同步枢纽)。

图5.17云计算服务软件的基础结构

说明:云计算服务软件分层的目的是分离主逻辑和通用模块,这样不仅能让主逻辑代码更清晰,还能抽离通用模块(复用代码)。

在整个云计算服务软件的结构中,除了任务执行主逻辑以外,大部分都是通用部分。也就是说,除了任务执行主逻辑以外,其他部分即为云计算服务软件的基础框架。

在开发不同业务场景的云计算服务软件时,只需要在这个基础框架之上,编写具体的任务执行逻辑和相关模块即可。至于基础框架的表现形式,可以封装起来(源码不可见),也可以保持代码裸露。

说明:框架是一类软件的基本骨架,构造框架的目的,是为了让开发者只关注具体业务场景的开发,而不需要关心通用部分的代码。至于框架的表现形式,需要根据实际情况而定,封装起来(源码不可见)有助于对外推广,保持代码裸露有利于开发人员查错。

本文给大家讲解的内容是大型网站架构的技术细节:云计算服务软件基础框架的构建

下篇文章给大家讲解的内容是大型网站架构的技术细节:云计算服务架构任务池与指令池的搭建和使用感谢大家的支持

OK,本文到此结束,希望对大家有所帮助。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平