当前位置:K88软件开发文章中心编程语言非主流编程语言Julia → 文章内容

Julia 控制流

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-15 16:28:09

处理异常比正常采用分支来处理,会慢得多。 try/catch 语句使用时也可以把异常赋值给某个变量。例如:julia> sqrt_second(x) = try sqrt(x[2]) catch y if isa(y, DomainError) sqrt(complex(x[2], 0)) elseif isa(y, BoundsError) sqrt(x) end endsqrt_second (generic function with 1 method)julia> sqrt_second([1 4])2.0julia> sqrt_second([1 -4])0.0 + 2.0imjulia> sqrt_second(9)3.0julia> sqrt_second(-9)ERROR: DomainError in sqrt_second at none:7 注意,跟在 catch 之后的符号会被解释为一个异常的名称,因此,需要注意的是,在单行中写 try/catch 表达式时。下面的代码将不会正常工作返回 x 的值为了防止发生错误:try bad() catch x end 我们在 catch 后使用分号或插入换行来实现:try bad() catch; x endtry bad()catch xend Julia 还提供了更高级的异常处理函数 rethrow , backtrace 和 catch_backtrace 。 finally 语句 在改变状态或者使用文件等资源时,通常需要在操作执行完成时做清理工作(比如关闭文件)。异常的存在使得这样的任务变得复杂,因为异常会导致程序提前退出。关键字 finally 可以解决这样的问题,无论程序是怎样退出的,finally 语句总是会被执行。 例如, 下面的程序说明了怎样保证打开的文件总是会被关闭:f = open("file")try # operate on file ffinally close(f)end 当程序执行完 try 语句块(例如因为执行到 return 语句,或者只是正常完成),close 语句将会被执行。如果 try 语句块因为异常提前退出,异常将会继续传播。catch 语句可以和 try,finally 一起使用。这时。finally 语句将会在 catch 处理完异常之后执行。 任务(也称为协程) 任务是一种允许计算灵活地挂起和恢复的控制流,有时也被称为对称协程、轻量级线程、协同多任务等。 如果一个计算(比如运行一个函数)被设计为 Task,有可能因为切换到其它 Task 而被中断。原先的 Task 在以后恢复时,会从原先中断的地方继续工作。切换任务不需要任何空间,同时可以有任意数量的任务切换,而不需要考虑堆栈问题。任务切换与函数调用不同,可以按照任何顺序来进行。 任务比较适合生产者-消费者模式,一个过程用来生产值,另一个用来消费值。消费者不能简单的调用生产者来得到值,因为两者的执行时间不一定协同。在任务中,两者则可以正常运行。 Julia 提供了 produce 和 consume 函数来解决这个问题。生产者调用 produce 函数来生产值:julia> function producer() produce("start") for n=1:4 produce(2n) end produce("stop") end; 要消费生产的值,先对生产者调用 Task 函数,然后对返回的对象重复调用 consume :julia> p = Task(producer);julia> consume(p)"start"julia> consume(p)2julia> consume(p)4julia> consume(p)6julia> consume(p)8julia> consume(p)"stop" 可以在 for 循环中迭代任务,生产的值被赋值给循环变量:julia> for x in Task(producer) println(x) endstart2468stop 注意 Task() 函数的参数,应为零参函数。生产者常常是参数化的,因此需要为其构造零参匿名函数 。可以直接写,也可以调用宏:function mytask(myarg) ...endtaskHdl = Task(() -> mytask(7))# 也可以写成taskHdl = @task mytask(7) produce 和 consume 但它并不在不同的 CPU 发起线程。我们将在并行计算中,讨论真正的内核线程。 核心任务操作 尽管 produce 和 consume 已经阐释了任务的本质,但是他们实际上是由库函数调用更原始的函数 yieldto 实现的。 yieldto(task,value) 挂起当前任务,切换到特定的 task , 并使这个 task 的最后一次 yeidlto 返回 \特定的 value。注意 yieldto 是唯一需要的操作来进行 ‘任务风格’的控制流;不需要调用和返回,我们只用在不同的任务之间切换即可。 这就是为什么这个特性被称做 “对称式协程”;每一个任务的切换都是用相同的机制。 yeildto 很强大, 但是大多数时候并不直接调用它。 当你从当前的任务切换走,你有可能会想切换回来, 但需要知道切换的时机和任务,这会需要相当的协调。 例如,procude 需要保持某个状态来记录消费者。无需手动地记录正在消费的任务让 produce 比 yieldto 更容易使用。 除此之外,为了高效地使用任务,其他一些基本的函数也同样必须。current_task() 获得当前运行任务的引用。istaskdone(t) 查询任务是否终止。istaskstarted(t) 查询任务是否启动。task_local_storage 处理当前任务的键值储存。 任务与事件 大多数任务的切换都是在等待像 I/O 请求这样的事件的时候,并由标准库的调度器完成。调度器记录正在运行的任务的队列,并执行一个循环来根据外部事件(比如消息到达)重启任务。 处理等待事件的基本函数是 wait。 有几种对象实现了 wait,比如对于 Process 对象, wait 会等待它终止。更多的时候 wait 是隐式的,比如 wait 可以发生在调用 read 的时候,等待数据变得可用。 在所有的情况中, wait 最终会操作在一个负责将任务排队和重启的 Condition 对象上。当任务在 Condition 上调用 wait, 任务会被标记为不可运行,被加入到 Condition 的 队列中,再切换至调度器。调度器会选取另一个任务来运行,或者等待外部事件。如果一切正常,最终一个事件句柄会在 Condition 上调用 notify,使正在等待的任务变得可以运行。 调用 Task 可以生成一个初始对调度器还未知的任务,这允许你用 yieldto 手动管理任务。不管怎样,当这样的任务正在等待事件时,事件一旦发生,它仍然会自动重启。而且任何时候你都可以调用 schedule(task) 或者用宏 @schedule 或 @async 来让调度器来运行一个任务,根本不用去等待任何事件。(参见并行计算) 任务状态 任务包含一个 state 域,它用来描述任务的执行状态。任务状态取如下的几种符号中的一种: 符号

上一页  [1] [2] [3] [4]  下一页


Julia 控制流