erlang学习:gen_server书上案例22.6练习题4

2024-08-23 23:52

本文主要是介绍erlang学习:gen_server书上案例22.6练习题4,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

昨天没有输出Fun中的io的原因是因为在任务函数中没有调用Fun方法,相当于只传了Fun函数但是没有进行调用,因此没有执行Fun函数,所以控制台中没有进行io的输出,今天在add_job中调用了Fun方法并执行,所以输出了相应的io。
同时今天增加了完成工作的字段,能够记录以及完成的工作数量,这样相较于昨天单纯的输出0来说,能够统计工人已经完成工作数,基本符合了书上第2个小问的要求

-module(gen_server_test).-export([start_link/0, add_job/2, work_wanted/0, job_done/1,statistics/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,terminate/2, code_change/3]).
-export([test_job_centre/0]).
-define(SERVER, ?MODULE).%%创建一个记录,记录中包含了一个工作队列,与下一个工作任务编号
-record(state,
{jobs = queue:new(),next_job_number = 1,workers = gb_sets:new(),completed_jobs = gb_sets:new()
}).start_link() ->gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
add_job(Fun,WorkerPid) ->gen_server:call(?SERVER, {add_job, Fun,WorkerPid}).
work_wanted() ->gen_server:call(?SERVER, work_wanted).
job_done(JobNumber) ->gen_server:call(?SERVER, {job_done, JobNumber}).
statistics() ->gen_server:call(?SERVER, statistics).
%%---------------------------------------------------------------------------
init([]) ->{ok, #state{}}.handle_call({add_job, Fun,WorkerPid}, _From, State) ->%%向队列中添加元素,队列的任务为执行Fun中的操作NewJobs = queue:in({State#state.next_job_number, Fun}, State#state.jobs),%%执行任务函数Fun(),NewWorkers = gb_sets:add(WorkerPid, State#state.workers),% 建立与工人的连接link(WorkerPid),{reply, State#state.next_job_number,State#state{jobs = NewJobs,next_job_number = State#state.next_job_number + 1,workers = NewWorkers}};
handle_call(work_wanted, _From, State) ->IsEmpty = queue:is_empty(State#state.jobs),case IsEmpty oftrue ->{reply, no, State};false ->{{value, {JobNumber, Fun}}, NewJobs} = queue:out(State#state.jobs),{reply, {JobNumber, Fun}, State#state{jobs = NewJobs}}end;
handle_call({job_done, JobNumber}, _From, State) ->NewCompletedJobs = gb_sets:add(JobNumber, State#state.completed_jobs),{reply, ok, State#state{completed_jobs = NewCompletedJobs}};
handle_call(statistics, _From, State) ->{reply, #{waiting => queue:len(State#state.jobs),in_progress => gb_sets:size(State#state.workers),completed => gb_sets:size(State#state.completed_jobs)}, State};
handle_call(Request, _From, State) ->{reply, {error, {unknown_request, Request}}, State}.handle_cast(_Msg, State) ->{noreply, State}.
handle_info({exit, WorkerPid, _Reason}, State) ->%% 如果工人进程崩溃,则从workers集合中移除,并将任务重新添加到队列NewWorkers = gb_sets:delete(WorkerPid, State#state.workers),%% 查找并重新添加任务{ok, Fun} = find_task_for_worker(WorkerPid, State#state.jobs),NewJobs = queue:in({State#state.next_job_number - 1, Fun}, State#state.jobs),{noreply, State#state{workers = NewWorkers, jobs = NewJobs}};
handle_info(_Info, State) ->{noreply, State}.terminate(_Reason, _State) ->ok.code_change(_OldVsn, State, _Extra) ->{ok, State}.find_task_for_worker(WorkerPid, Jobs) ->case gb_sets:is_member(WorkerPid, gb_sets:from_list(Jobs)) oftrue ->%% 找到对应的任务{value, {_, Fun}} = queue:out(Jobs),{ok, Fun};false ->{error, not_found}end.
test_job_centre() ->%%创建两个任务Job1 = fun() ->io:format("Doing job 1~n")end,Job2 = fun() ->io:format("Doing job 2~n")end,%%将两个任务放入任务队列中JobNumber1 = add_job(Job1,self()),io:format("Job number 1 is ~p~n", [JobNumber1]),JobNumber2 = add_job(Job2,self()),io:format("Job number 2 is ~p~n", [JobNumber2]),%%工人领取任务队列任务{JobNumber1, Job1} = work_wanted(),io:format("Doing job ~p~n", [{JobNumber1,Job1}]),{JobNumber2, Job2} = work_wanted(),io:format("Doing job ~p~n", [{JobNumber2,Job2}]),job_done(JobNumber1),job_done(JobNumber2),case work_wanted() ofno ->io:format("No more jobs~n");{JobNumber, Job} ->io:format("Doing job ~p~n", [{JobNumber,Job}])end,exit(normal),ok.

输出结果为:
请添加图片描述

这篇关于erlang学习:gen_server书上案例22.6练习题4的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1100859

相关文章

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

使用Navicat工具比对两个数据库所有表结构的差异案例详解

《使用Navicat工具比对两个数据库所有表结构的差异案例详解》:本文主要介绍如何使用Navicat工具对比两个数据库test_old和test_new,并生成相应的DDLSQL语句,以便将te... 目录概要案例一、如图两个数据库test_old和test_new进行比较:二、开始比较总结概要公司存在多

查询SQL Server数据库服务器IP地址的多种有效方法

《查询SQLServer数据库服务器IP地址的多种有效方法》作为数据库管理员或开发人员,了解如何查询SQLServer数据库服务器的IP地址是一项重要技能,本文将介绍几种简单而有效的方法,帮助你轻松... 目录使用T-SQL查询方法1:使用系统函数方法2:使用系统视图使用SQL Server Configu

SQL Server数据库迁移到MySQL的完整指南

《SQLServer数据库迁移到MySQL的完整指南》在企业应用开发中,数据库迁移是一个常见的需求,随着业务的发展,企业可能会从SQLServer转向MySQL,原因可能是成本、性能、跨平台兼容性等... 目录一、迁移前的准备工作1.1 确定迁移范围1.2 评估兼容性1.3 备份数据二、迁移工具的选择2.1

SQL Server使用SELECT INTO实现表备份的代码示例

《SQLServer使用SELECTINTO实现表备份的代码示例》在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误,在SQLServer中,可以使用SELECTINT... 在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SE

SpringBoot实现动态插拔的AOP的完整案例

《SpringBoot实现动态插拔的AOP的完整案例》在现代软件开发中,面向切面编程(AOP)是一种非常重要的技术,能够有效实现日志记录、安全控制、性能监控等横切关注点的分离,在传统的AOP实现中,切... 目录引言一、AOP 概述1.1 什么是 AOP1.2 AOP 的典型应用场景1.3 为什么需要动态插

Window Server创建2台服务器的故障转移群集的图文教程

《WindowServer创建2台服务器的故障转移群集的图文教程》本文主要介绍了在WindowsServer系统上创建一个包含两台成员服务器的故障转移群集,文中通过图文示例介绍的非常详细,对大家的... 目录一、 准备条件二、在ServerB安装故障转移群集三、在ServerC安装故障转移群集,操作与Ser

Golang操作DuckDB实战案例分享

《Golang操作DuckDB实战案例分享》DuckDB是一个嵌入式SQL数据库引擎,它与众所周知的SQLite非常相似,但它是为olap风格的工作负载设计的,DuckDB支持各种数据类型和SQL特性... 目录DuckDB的主要优点环境准备初始化表和数据查询单行或多行错误处理和事务完整代码最后总结Duck

MySQL不使用子查询的原因及优化案例

《MySQL不使用子查询的原因及优化案例》对于mysql,不推荐使用子查询,效率太差,执行子查询时,MYSQL需要创建临时表,查询完毕后再删除这些临时表,所以,子查询的速度会受到一定的影响,本文给大家... 目录不推荐使用子查询和JOIN的原因解决方案优化案例案例1:查询所有有库存的商品信息案例2:使用EX

SQL Server数据库磁盘满了的解决办法

《SQLServer数据库磁盘满了的解决办法》系统再正常运行,我还在操作中,突然发现接口报错,后续所有接口都报错了,一查日志发现说是数据库磁盘满了,所以本文记录了SQLServer数据库磁盘满了的解... 目录问题解决方法删除数据库日志设置数据库日志大小问题今http://www.chinasem.cn天发